ฉันจะบอก gcc ไม่ให้อินไลน์ฟังก์ชันได้อย่างไร


126

สมมติว่าฉันมีฟังก์ชันเล็ก ๆ นี้ในไฟล์ต้นฉบับ

static void foo() {}

และฉันสร้างไบนารีเวอร์ชันที่ปรับให้เหมาะสมแล้ว แต่ฉันไม่ต้องการให้ฟังก์ชันนี้อินไลน์ (เพื่อวัตถุประสงค์ในการเพิ่มประสิทธิภาพ) มีมาโครที่ฉันสามารถเพิ่มในซอร์สโค้ดเพื่อป้องกันการแทรกในหรือไม่


ขอบคุณสำหรับคำถามนี้! ฉันทำโปรไฟล์ด้วย oprofile เมื่อฟังก์ชันไม่ปรากฏขึ้นคำตอบที่นี่แก้ไขสิ่งนี้
Simon A. Eugster

คำตอบ:


149

คุณต้องการแอตทริบิวต์gcc-specificnoinline

แอตทริบิวต์ของฟังก์ชันนี้ป้องกันไม่ให้พิจารณาฟังก์ชันสำหรับการแทรกใน หากฟังก์ชันไม่มีผลข้างเคียงจะมีการเพิ่มประสิทธิภาพอื่น ๆ นอกเหนือจากการแทรกในที่ทำให้การเรียกฟังก์ชันถูกปรับให้เหมาะสมแม้ว่าการเรียกฟังก์ชันจะใช้งานอยู่ เพื่อป้องกันไม่ให้การโทรดังกล่าวถูกเพิ่มประสิทธิภาพให้วาง asm ("");

ใช้แบบนี้:

void __attribute__ ((noinline)) foo() 
{
  ...
}

32
เมื่อใช้ gcc 4.4.3 บน Arch Linux ฉันได้รับข้อผิดพลาดทางไวยากรณ์พร้อมกับแอตทริบิวต์ที่วางไว้ด้านบน ทำงานได้อย่างถูกต้องเมื่ออยู่ข้างหน้าฟังก์ชัน (เช่นแอตทริบิวต์ ((noinline)) void foo () {})
mrkj

2
Arduino ยังต้องการให้วางไว้ก่อนฟังก์ชัน
Peter N Lewis

2
แก้ไขเพื่อแก้ไขไวยากรณ์ของแอตทริบิวต์
Quuxplusone

1
โครงสร้าง asm ("") เป็นแบบข้ามแพลตฟอร์มและทำงานได้สำเร็จ ฉันทำเพื่อ x86 Linux และไม่ทำให้เกิดปัญหาการสร้างบน PowerPC AIX ขอบคุณสำหรับคำแนะนำที่เป็นประโยชน์นี้!
Marty

1
แนวทางที่ต้องมีการเปลี่ยนแปลงโค้ดทุกที่ไม่สามารถถือเป็นคำตอบที่ยอมรับได้
ajeh

31

GCC มีสวิตช์ที่เรียกว่า

-fno-inline-small-functions

ดังนั้นใช้เมื่อเรียกใช้ gcc แต่ผลข้างเคียงคือฟังก์ชั่นเล็ก ๆ อื่น ๆ ทั้งหมดจะไม่อยู่ในบรรทัดเดียวกัน


ไม่ทำงานในระดับคอมไพเลอร์ กำลังใช้ gcc 5.2.1 20150902 (Red Hat 5.2.1-2)
John Greene

GCC 6.4 ในปัจจุบันเสียหรือสิ่งนี้และง่ายกว่า-fno-inlineนั้นไม่ได้ผลเลย gdbยังคงเข้าสู่วิธีการในขั้นตอน gdbสิ่งที่เสียและผมสงสัยว่ามันคือ
ajeh

จะปิดการเพิ่มประสิทธิภาพแบบอินไลน์สำหรับทุกคนไม่เพียง แต่สำหรับฟังก์ชันที่ระบุเท่านั้น
23

@ajeh ฟังก์ชั่น inlining ไม่ได้หมายความว่าจะถูกเรียกตามปกติใช่หรือไม่?
Melebius

21

วิธีแบบพกพาในการทำเช่นนี้คือการเรียกใช้ฟังก์ชันผ่านตัวชี้:

void (*foo_ptr)() = foo;
foo_ptr();

แม้ว่าสิ่งนี้จะให้คำแนะนำที่แตกต่างกันไปในการแยกสาขาซึ่งอาจไม่ใช่เป้าหมายของคุณ ซึ่งจะทำให้เป็นจุดที่ดี: สิ่งที่เป็นเป้าหมายของคุณที่นี่?


2
หากตัวชี้ถูกกำหนดไว้ที่ขอบเขตไฟล์ไม่ใช่แบบคงที่ควรใช้งานได้เนื่องจากคอมไพเลอร์ไม่สามารถถือว่ามีค่าเริ่มต้นในขณะใช้งาน ถ้าเป็นคนในพื้นที่ (ตามที่แสดง) จะถือว่าเหมือนกับ foo () ("ในทศวรรษนี้" เขากล่าวเสริมโดยดูวันที่)
greggo

16

ฉันรู้ว่าคำถามนั้นเกี่ยวกับ GCC แต่ฉันคิดว่าการมีข้อมูลเกี่ยวกับคอมไพเลอร์คอมไพเลอร์อื่น ๆ อาจเป็นประโยชน์เช่นกัน

noinline คุณลักษณะฟังก์ชันของ GCC นั้นค่อนข้างเป็นที่นิยมในหมู่คอมไพเลอร์อื่น ๆ เช่นกัน ได้รับการสนับสนุนอย่างน้อย:

  • เสียงดัง (ตรวจสอบด้วย__has_attribute(noinline))
  • คอมไพเลอร์ Intel C / C ++ (เอกสารของพวกเขาแย่มาก แต่ฉันมั่นใจว่ามันใช้ได้กับ 16.0+)
  • Oracle Solaris Studio กลับไปเป็นอย่างน้อย 12.2
  • ARM C / C ++ คอมไพเลอร์กลับไปเป็นอย่างน้อย 4.1
  • IBM XL C / C ++ กลับไปเป็นอย่างน้อย 10.1
  • TI 8.0+ (หรือ 7.3+ พร้อม --gcc ซึ่งจะกำหนด__TI_GNU_ATTRIBUTE_SUPPORT__)

นอกจากนี้ MSVC ยังสนับสนุน __declspec(noinline) กลับไปที่ Visual Studio 7.1 Intel อาจรองรับด้วย (พยายามเข้ากันได้กับทั้ง GCC และ MSVC) แต่ฉันไม่ได้ใส่ใจที่จะตรวจสอบสิ่งนั้น โดยพื้นฐานแล้วไวยากรณ์จะเหมือนกัน:

__declspec(noinline)
static void foo(void) { }

PGI 10.2+ (และอาจเก่ากว่า) รองรับnoinlinepragma ซึ่งใช้กับฟังก์ชันถัดไป:

#pragma noinline
static void foo(void) { }

TI 6.0+ รองรับ FUNC_CANNOT_INLINE pragma ซึ่ง (น่ารำคาญ) ทำงานต่างกันใน C และ C ++ ใน C ++ คล้ายกับของ PGI:

#pragma FUNC_CANNOT_INLINE;
static void foo(void) { }

อย่างไรก็ตามใน C ต้องระบุชื่อฟังก์ชัน:

#pragma FUNC_CANNOT_INLINE(foo);
static void foo(void) { }

Cray 6.4+ (และอาจก่อนหน้านี้) ใช้แนวทางที่คล้ายกันโดยต้องใช้ชื่อฟังก์ชัน:

#pragma _CRI inline_never foo
static void foo(void) { }

Oracle Developer Studio ยังสนับสนุน pragma ซึ่งใช้ชื่อฟังก์ชันโดยย้อนกลับไปอย่างน้อย Forte Developer 6แต่โปรดทราบว่าจำเป็นต้องมาหลังจากการประกาศแม้ในเวอร์ชันล่าสุด:

static void foo(void);
#pragma no_inline(foo)

ขึ้นอยู่กับว่าคุณทุ่มเทแค่ไหนคุณสามารถสร้างมาโครที่จะทำงานได้ทุกที่ แต่คุณจะต้องมีชื่อฟังก์ชันและการประกาศเป็นอาร์กิวเมนต์

ถ้า OTOH คุณโอเคกับบางสิ่งบางอย่างที่เหมาะกับคนส่วนใหญ่คุณสามารถหลีกหนีจากสิ่งที่น่าพึงพอใจเล็กน้อยและไม่จำเป็นต้องทำซ้ำตัวเอง นั่นคือแนวทางที่ฉันใช้สำหรับHedleyซึ่งเวอร์ชันปัจจุบันของ HEDLEY_NEVER_INLINE มีลักษณะดังนี้:

#if \
  HEDLEY_GNUC_HAS_ATTRIBUTE(noinline,4,0,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
#  define HEDLEY_NEVER_INLINE __attribute__((__noinline__))
#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
#  define HEDLEY_NEVER_INLINE __declspec(noinline)
#elif HEDLEY_PGI_VERSION_CHECK(10,2,0)
#  define HEDLEY_NEVER_INLINE _Pragma("noinline")
#elif HEDLEY_TI_VERSION_CHECK(6,0,0)
#  define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
#else
#  define HEDLEY_NEVER_INLINE HEDLEY_INLINE
#endif

หากคุณไม่ต้องการใช้ Hedley (เป็นโดเมนสาธารณะเดียว / ส่วนหัว CC0) คุณสามารถแปลงมาโครการตรวจสอบเวอร์ชันได้โดยไม่ต้องใช้ความพยายามมากเกินไป แต่ฉันเต็มใจที่จะใส่มากกว่า☺


ขอบคุณสำหรับลิงก์ไปยังโครงการของคุณ @nemequ ฉันได้ขอให้นักพัฒนาซอฟต์แวร์รายอื่นประเมินการใช้งานของเรา เรามีสถาปัตยกรรมที่หลากหลาย
Daisuke Aramaki

ฉันสนใจมากที่จะรู้ว่าพวกเขาพูดอะไรโดยเฉพาะอย่างยิ่งถ้าพวกเขาไม่สนใจ และแน่นอนว่าฉันพร้อมที่จะตอบคำถาม (ตัวติดตามปัญหา GitHub อีเมลอะไรก็ได้ ... )
nemequ




2

ฉันทำงานกับ gcc 7.2 ฉันต้องการฟังก์ชั่นที่ไม่อินไลน์เป็นพิเศษเพราะต้องสร้างอินสแตนซ์ในไลบรารี ฉันลอง__attribute__((noinline))คำตอบและasm("")คำตอบ ไม่มีใครแก้ปัญหา

สุดท้ายฉันพบว่าการกำหนดตัวแปรคงที่ภายในฟังก์ชันจะบังคับให้คอมไพเลอร์จัดสรรพื้นที่สำหรับมันในบล็อกตัวแปรแบบคงที่และออกการกำหนดค่าเริ่มต้นเมื่อมีการเรียกใช้ฟังก์ชันครั้งแรก

นี่เป็นกลอุบายสกปรก แต่ได้ผล


คุณสามารถกำหนดฟังก์ชันของคุณinline void foo(void) { ... }ในส่วนหัวและประกาศextern inline void foo(void);ในไฟล์ซอร์สของไลบรารี ตามความหมาย C99 คอมไพเลอร์จะได้รับอนุญาตให้อินไลน์ฟังก์ชันเมื่อถูกใจและปล่อยอ็อบเจ็กต์โค้ดในไลบรารีของคุณ ดู"inline" โดยไม่มี "static" หรือ "extern" จะมีประโยชน์ใน C99 หรือไม่ .
diapir
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.