Gcc'ye bir işlevi satır içi yapmamasını nasıl söyleyebilirim?


126

Bir kaynak dosyada bu küçük işlevim olduğunu varsayalım

static void foo() {}

ve ikili programımın optimize edilmiş bir sürümünü oluşturuyorum, ancak bu işlevin satır içi olmasını istemiyorum (optimizasyon amaçları için). Inlineing'i önlemek için bir kaynak koduna ekleyebileceğim bir makro var mı?


Bu soru için teşekkürler! Bir işlev görünmediğinde oprofile ile profil oluşturuyordum, buradaki cevaplar bunu düzeltti.
Simon A.Eugster

Yanıtlar:


149

gcc-Specific noinlineözniteliğini istiyorsunuz .

Bu işlev özelliği, bir işlevin satır içi olarak dikkate alınmasını engeller. İşlevin yan etkileri yoksa, işlev çağrılarının canlı olmasına rağmen işlev çağrılarının optimize edilmesine neden olan satır içi dışında optimizasyonlar vardır. Bu tür aramaların optimize edilmesini engellemek için asm ("");

Bunu şu şekilde kullanın:

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

32
Arch Linux'ta gcc 4.4.3'ü kullanarak, yukarıda belirtilen öznitelikle bir sözdizimi hatası alıyorum.
İşlevden

2
Arduino da işlevin önüne yerleştirilmesini istedi.
Peter N Lewis

2
Öznitelik sözdizimini düzeltmek için düzenlendi.
Quuxplusone

1
Asm ("") yapısı aslında oldukça çapraz platformdur ve işi tamamlamıştır. Bunu x86 Linux için yaptım ve PowerPC AIX üzerinde bir derleme sorununa neden olmadı. Bu faydalı öneri için teşekkürler!
Marty

1
Her yerde kod değişiklikleri gerektiren yaklaşım makul olarak kabul edilebilir bir cevap olarak kabul edilemez.
ajeh

31

GCC'nin adında bir anahtarı var

-fno-inline-small-functions

Bu yüzden gcc'yi çağırırken bunu kullanın. Ancak yan etki, diğer tüm küçük işlevlerin de satır içi olmamasıdır.


Derleyici düzeyinde çalışmadı. Gcc 5.2.1 20150902 (Red Hat 5.2.1-2)
John Greene kullanıyordu

Ya mevcut GCC 6.4 bozuldu ya da bu ve daha basit -fno-inlinehiç çalışmıyor. gdbhala yöntemlere adım adım giriyor. Bir şey kırıldı ve olduğundan şüpheliyim gdb.
ajeh

Yalnızca belirli bir işlev için değil, herkes için satır içi optimizasyonu kapatır.
23

@ajeh İşlevleri satır içine almamak, normal olarak çağrıldıkları anlamına gelir, değil mi?
Melebius

21

Bunu yapmanın taşınabilir bir yolu, işlevi bir işaretçi aracılığıyla çağırmaktır:

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

Bu, dallanma için farklı talimatlar üretmesine rağmen, bu sizin amacınız olmayabilir. Ne: Hangi iyi noktaya getirir olan hedef burada?


2
İşaretçi dosya kapsamında tanımlanmışsa ve statik değilse, çalışmalıdır çünkü derleyici kullanım anında başlangıç ​​değerine sahip olduğunu varsayamaz. Yerel ise (gösterildiği gibi) neredeyse kesinlikle foo () ile aynı muamele görür. ("Bu on yılda", tarihlere bakarak ekledi)
greggo

16

Sorunun GCC ile ilgili olduğunu biliyorum, ancak diğer derleyiciler hakkında da derleyiciler hakkında biraz bilgi sahibi olmanın yararlı olabileceğini düşündüm.

GCC'nin noinline işlev özniteliği diğer derleyiciler arasında da oldukça popülerdir. En azından aşağıdakiler tarafından desteklenir:

  • Clang (kontrol edin __has_attribute(noinline))
  • Intel C / C ++ Derleyici (belgeleri berbat, ancak 16.0+ üzerinde çalıştığından eminim)
  • Oracle Solaris Studio, en az 12.2'ye geri döndü
  • ARM C / C ++ Derleyici en az 4.1'e geri döndü
  • IBM XL C / C ++ en az 10.1'e geri döndü
  • TI 8.0+ (veya 7.3+ --gcc ile tanımlanacak __TI_GNU_ATTRIBUTE_SUPPORT__)

Ayrıca MSVC, __declspec(noinline) Visual Studio 7.1'e geri dönmeyi destekler . Intel muhtemelen bunu da destekliyor (hem GCC hem de MSVC ile uyumlu olmaya çalışıyorlar), ancak bunu doğrulamaya zahmet etmedim. Sözdizimi temelde aynıdır:

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

PGI 10.2+ (ve muhtemelen daha eski), bir noinlinesonraki işlev için geçerli olan bir pragmayı destekler :

#pragma noinline
static void foo(void) { }

TI 6.0+, FUNC_CANNOT_INLINE (sinir bozucu) C ve C ++ 'da farklı çalışan bir pragmayı destekler . C ++ 'da, PGI'lara benzer:

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

Bununla birlikte, C'de işlev adı gereklidir:

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

Cray 6.4+ (ve muhtemelen daha erken), işlev adını gerektiren benzer bir yaklaşımı benimser:

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

Oracle Developer Studio ayrıca en azından Forte Developer 6'ya geri dönen işlev adını alan bir pragmayı destekler , ancak son sürümlerde bile bildirimden sonra gelmesi gerektiğini unutmayın :

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

Kendinize ne kadar adanmış olduğunuza bağlı olarak, her yerde çalışacak bir makro oluşturabilirsiniz, ancak işlev adı ve bağımsız değişken olarak bildirime sahip olmanız gerekir.

OTOH, çoğu insan için işe yarayan bir şeyden memnunsanız, estetik açıdan biraz daha hoş olan ve kendinizi tekrarlamayı gerektirmeyen bir şeyden kurtulabilirsiniz. HEDLEY_NEVER_INLINE'ın mevcut sürümünün şuna benzediği Hedley için benimsediğim yaklaşım budur :

#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'i kullanmak istemiyorsanız (bu tek bir genel etki alanı / CC0 başlığı), sürüm kontrol makrolarını çok fazla çaba harcamadan, ancak benim ☺ koymaya istekli olduğumdan daha fazlasını dönüştürebilirsiniz.


Projenizin bağlantısı için teşekkürler @nemequ. Diğer geliştiricilerimizden kullanımımız için değerlendirmelerini istedim. Farklı mimarilerimiz var.
Daisuke Aramaki

Ne dediklerini bilmek isterim, özellikle de ilgilenmezlerse. Ve tabii ki, soruları yanıtlamak üzereyim (GitHub sorun izleyicisi, e-posta, her neyse ...).
nemequ

14

İçin bir derleyici hatası __attribute__((noinline))alırsanız, deneyebilirsiniz:

noinline int func(int arg)
{
    ....
}

10
static __attribute__ ((noinline))  void foo()
{

}

Bu benim için çalıştı.


8

Kullan noinline özelliği :

int func(int arg) __attribute__((noinline))
{
}

Muhtemelen hem fonksiyonu harici kullanım için ilan ederken hem de fonksiyonu yazarken kullanmalısınız.


2

Gcc 7.2 ile çalışıyorum. Satır içi olmayan bir işleve özellikle ihtiyacım vardı, çünkü bir kitaplıkta somutlaştırılması gerekiyordu. __attribute__((noinline))Cevabı olduğu kadar cevabı da denedim asm(""). Hiçbiri sorunu çözmedi.

Son olarak, fonksiyonun içinde statik bir değişken tanımlamanın, derleyiciyi statik değişken bloğunda onun için yer ayırmaya ve fonksiyon ilk çağrıldığında onun için bir başlatma yapmaya zorlayacağını düşündüm.

Bu biraz kirli bir numara ama işe yarıyor.


İşlevinizi inline void foo(void) { ... }bir başlıkta tanımlayabilir ve extern inline void foo(void);bir kitaplık kaynak dosyasında bildirebilirsiniz. C99 semantiğini takiben, derleyicinin kitaplığınızdaki nesne kodunu istediği VE yaydığı zaman işlevi satır içi yapmasına izin verilir. Bkz Is "statik" veya C99 hiç "extern" yararlı olmadan "inline"? .
diapir
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.