Jak mogę powiedzieć gcc, aby nie wstawiał funkcji?


126

Powiedzmy, że mam tę małą funkcję w pliku źródłowym

static void foo() {}

i buduję zoptymalizowaną wersję mojego pliku binarnego, ale nie chcę, aby ta funkcja była wbudowana (do celów optymalizacji). czy istnieje makro, które mogę dodać w kodzie źródłowym, aby zapobiec wstawianiu?


Dzięki za to pytanie! Profilowałem za pomocą oprofile, gdy funkcja nie pojawiła się, odpowiedzi tutaj naprawiły.
Simon A. Eugster

Odpowiedzi:


149

Chcesz mieć atrybut gcc-specific noinline.

Ten atrybut funkcji zapobiega rozpatrywaniu funkcji do wstawiania. Jeśli funkcja nie ma skutków ubocznych, istnieją optymalizacje inne niż wstawianie, które powodują, że wywołania funkcji są optymalizowane, mimo że wywołanie funkcji jest aktywne. Aby zapobiec optymalizacji takich połączeń, ustaw asm ("");

Użyj tego w ten sposób:

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

32
Używając gcc 4.4.3 w Arch Linux, otrzymuję błąd składni z atrybutem umieszczonym jak powyżej. To działa prawidłowo, gdy poprzedza funkcję (np atrybut ((noinline)) void foo () {})
mrkj

2
Arduino chciał również umieścić go przed funkcją.
Peter N Lewis,

2
Edytowano, aby poprawić składnię atrybutu.
Quuxplusone

1
Konstrukcja asm („”) jest w rzeczywistości dość wieloplatformowa i spełnia swoje zadanie. Zrobiłem to dla Linuxa x86 i nie spowodowało to problemu z kompilacją na PowerPC AIX. Dzięki za tę przydatną sugestię!
Marty

1
Podejścia, które wymaga zmian w kodzie wszędzie, nie można rozsądnie uznać za akceptowalną odpowiedź.
ajeh

31

GCC ma przełącznik o nazwie

-fno-inline-small-functions

Więc użyj tego podczas wywoływania gcc. Ale efektem ubocznym jest to, że wszystkie inne małe funkcje również nie są wbudowane.


Nie działał na poziomie kompilatora. Używałem gcc 5.2.1 20150902 (Red Hat 5.2.1-2)
John Greene,

Albo obecne GCC 6.4 jest zepsute, albo to i prostsze w -fno-inlineogóle nie działa. gdbnadal wprowadza metody po przejściu. Coś jest zepsute i wątpię, że tak jest gdb.
ajeh

Wyłączy to wbudowaną optymalizację dla wszystkich, nie tylko dla określonej funkcji.
gdzie

@ajeh Brak funkcji wbudowanych oznacza, że ​​są wywoływane normalnie, prawda?
Melebius

21

Przenośnym sposobem na to jest wywołanie funkcji za pomocą wskaźnika:

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

Chociaż powoduje to różne instrukcje rozgałęziania, co może nie być twoim celem. Co daje dobry punkt: jaki jest twój cel?


2
Jeśli wskaźnik jest zdefiniowany w zakresie pliku, a nie statyczny, powinien działać, ponieważ kompilator nie może wtedy założyć, że ma swoją początkową wartość w momencie użycia. Jeśli jest lokalna (jak pokazano), prawie na pewno jest traktowana tak samo jak foo (). („W tej dekadzie”, dodał, patrząc na daty)
greggo

16

Wiem, że pytanie dotyczy GCC, ale pomyślałem, że może przydać się trochę informacji o kompilatorach i innych kompilatorach.

noinline Atrybut funkcji GCC jest dość popularny również w innych kompilatorach. Jest wspierany przez co najmniej:

  • Clang (sprawdź z __has_attribute(noinline))
  • Kompilator Intel C / C ++ (ich dokumentacja jest okropna, ale jestem pewien, że działa na 16.0+)
  • Oracle Solaris Studio z powrotem do co najmniej 12.2
  • Kompilator ARM C / C ++ z powrotem do co najmniej 4.1
  • IBM XL C / C ++ z powrotem do co najmniej 10.1
  • TI 8.0+ (lub 7.3+ z --gcc, które zdefiniuje __TI_GNU_ATTRIBUTE_SUPPORT__)

Ponadto MSVC obsługuje __declspec(noinline) powrót do programu Visual Studio 7.1. Intel prawdopodobnie też to obsługuje (starają się być kompatybilne zarówno z GCC, jak i MSVC), ale nie zadałem sobie trudu, aby to zweryfikować. Składnia jest w zasadzie taka sama:

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

PGI 10.2+ (i prawdopodobnie starsze) wspiera noinlinepragmę, która odnosi się do następnej funkcji:

#pragma noinline
static void foo(void) { }

TI 6.0+ wspiera FUNC_CANNOT_INLINE pragmę, która (irytująco) działa inaczej w C i C ++. W C ++ jest podobny do PGI:

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

W C jednak nazwa funkcji jest wymagana:

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

Cray 6.4+ (i prawdopodobnie wcześniej) przyjmuje podobne podejście, wymagając nazwy funkcji:

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

Oracle Developer Studio obsługuje również pragmę, która przyjmuje nazwę funkcji, cofając się co najmniej do Forte Developer 6 , ale pamiętaj, że musi ona nastąpić po deklaracji, nawet w najnowszych wersjach:

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

W zależności od tego, jak bardzo jesteś oddany, możesz utworzyć makro, które działałoby wszędzie, ale musisz mieć nazwę funkcji, a także deklarację jako argumenty.

Jeśli, OTOH, nie przeszkadza ci coś, co po prostu działa dla większości ludzi, możesz uciec z czymś, co jest trochę bardziej estetyczne i nie wymaga powtarzania się. Oto podejście, które zastosowałem w przypadku Hedley , gdzie obecna wersja HEDLEY_NEVER_INLINE wygląda tak:

#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

Jeśli nie chcesz używać Hedley (to pojedynczy nagłówek domeny publicznej / CC0), możesz przekonwertować makra sprawdzające wersję bez zbytniego wysiłku, ale więcej, niż jestem gotów włożyć ☺.


Dzięki za link do twojego projektu @nemequ. Poprosiłem naszych innych programistów, aby ocenili go pod kątem naszego użytku. Mamy zróżnicowaną architekturę.
Daisuke Aramaki

Byłbym bardzo zainteresowany tym, co mówią, zwłaszcza jeśli nie są zainteresowani. I oczywiście jestem w pobliżu, aby odpowiadać na pytania (narzędzie do śledzenia problemów GitHub, e-mail, cokolwiek…).
nemequ

14

Jeśli pojawi się błąd kompilatora dla __attribute__((noinline)), możesz po prostu spróbować:

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

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

}

To właśnie zadziałało dla mnie.


8

Użyj noinline atrybutu :

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

Prawdopodobnie powinieneś go używać zarówno podczas deklarowania funkcji do użytku zewnętrznego, jak i podczas pisania funkcji.


2

Pracuję z gcc 7.2. W szczególności potrzebowałem funkcji nieliniowej, ponieważ musiała zostać utworzona w bibliotece. Spróbowałem __attribute__((noinline))odpowiedzi, a takżeasm("") odpowiedzi. Żaden z nich nie rozwiązał problemu.

Wreszcie doszedłem do wniosku, że zdefiniowanie zmiennej statycznej wewnątrz funkcji zmusi kompilator do przydzielenia dla niej miejsca w bloku zmiennej statycznej i do zainicjowania jej przy pierwszym wywołaniu funkcji.

To trochę brudna sztuczka, ale działa.


Możesz zdefiniować swoją funkcję inline void foo(void) { ... }w nagłówku i zadeklarować ją extern inline void foo(void);w pliku źródłowym biblioteki. Zgodnie z semantyką C99, kompilator będzie mógł wbudować funkcję, kiedy zechce ORAZ wyemitować kod obiektowy w twojej bibliotece. Zobacz Czy „inline” bez „static” lub „extern” jest kiedykolwiek przydatne w C99? .
diapir
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.