Wie kann ich gcc anweisen, eine Funktion nicht zu inline?

126

Angenommen, ich habe diese kleine Funktion in einer Quelldatei

static void foo() {}

und ich erstelle eine optimierte Version meiner Binärdatei, möchte diese Funktion jedoch nicht inline (für Optimierungszwecke). Gibt es ein Makro, das ich einem Quellcode hinzufügen kann, um das Inlining zu verhindern?

vehomzzz
quelle
Danke für diese Frage! Ich habe mit oprofile profiliert, als eine Funktion nicht angezeigt wurde. Die Antworten hier haben dies behoben.
Simon A. Eugster
c ++: stackoverflow.com/questions/3329214/…
Ciro Santilli 17 冠状 病 六四 事件 17

Antworten:

149

Sie möchten das Attribut gcc-spezifisch noinline.

Dieses Funktionsattribut verhindert, dass eine Funktion für das Inlining berücksichtigt wird. Wenn die Funktion keine Nebenwirkungen hat, gibt es andere Optimierungen als Inlining, die dazu führen, dass Funktionsaufrufe wegoptimiert werden, obwohl der Funktionsaufruf aktiv ist. Um zu verhindern, dass solche Anrufe optimiert werden, setzen Sie asm ("");

Verwenden Sie es so:

void __attribute__ ((noinline)) foo() 
{
  ...
}
Alex Kribbeln
quelle
32
Bei Verwendung von gcc 4.4.3 unter Arch Linux wird ein Syntaxfehler mit dem oben platzierten Attribut angezeigt. Es funktioniert korrekt, wenn es vor der Funktion steht (z. B. Attribut ((noinline)) void foo () {})
mrkj
2
Arduino wollte es auch vor der Funktion platzieren.
Peter N Lewis
2
Bearbeitet, um die Attributsyntax zu korrigieren.
Quuxplusone
1
Das asm ("") - Konstrukt ist eigentlich ziemlich plattformübergreifend und hat die Arbeit erledigt. Ich habe es für x86 Linux gemacht und es hat kein Build-Problem unter PowerPC AIX verursacht. Vielen Dank für diesen nützlichen Vorschlag!
Marty
1
Der Ansatz, der überall Codeänderungen erfordert, kann nicht als akzeptable Antwort angesehen werden.
Ajeh
31

GCC hat einen Schalter namens

-fno-inline-small-functions

Verwenden Sie dies also, wenn Sie gcc aufrufen. Der Nebeneffekt ist jedoch, dass alle anderen kleinen Funktionen ebenfalls nicht inline sind.

lukmac
quelle
Hat auf Compilerebene nicht funktioniert. Verwendete gcc 5.2.1 20150902 (Red Hat 5.2.1-2)
John Greene
Entweder ist das aktuelle GCC 6.4 defekt, oder dies und einfacher -fno-inlinefunktionieren überhaupt nicht. gdbgibt immer noch Methoden beim Step-Over ein. Etwas ist kaputt und ich bezweifle es gdb.
Ajeh
Die Inline-Optimierung wird für alle deaktiviert, nicht nur für eine bestimmte Funktion.
wo23
@ajeh Nicht inlining Funktionen bedeutet, dass sie normal aufgerufen werden, nicht wahr?
Melebius
21

Eine tragbare Möglichkeit, dies zu tun, besteht darin, die Funktion über einen Zeiger aufzurufen:

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

Dies führt jedoch zu unterschiedlichen Anweisungen zum Verzweigen, was möglicherweise nicht Ihr Ziel ist. Was einen guten Punkt aufwirft: Was ist Ihr Ziel hier?

Andy Ross
quelle
2
Wenn der Zeiger im Dateibereich definiert und nicht statisch ist, sollte er funktionieren, da der Compiler dann nicht davon ausgehen kann, dass er zum Zeitpunkt der Verwendung seinen Anfangswert hat. Wenn es ein Lokal ist (wie gezeigt), wird es mit ziemlicher Sicherheit genauso behandelt wie foo (). ("In diesem Jahrzehnt", fügte er hinzu und schaute auf die Daten)
Greggo
16

Ich weiß, dass es sich bei der Frage um GCC handelt, aber ich dachte, es könnte nützlich sein, einige Informationen über Compiler und andere Compiler zu haben.

Das noinline Funktionsattribut von GCC ist auch bei anderen Compilern sehr beliebt. Es wird unterstützt von mindestens:

  • Clang (erkundigen Sie sich bei __has_attribute(noinline))
  • Intel C / C ++ Compiler (ihre Dokumentation ist schrecklich, aber ich bin sicher, dass sie unter 16.0+ funktioniert)
  • Oracle Solaris Studio zurück auf mindestens 12.2
  • ARM C / C ++ Compiler zurück auf mindestens 4.1
  • IBM XL C / C ++ zurück auf mindestens 10.1
  • TI 8.0+ (oder 7.3+ mit --gcc, was definiert __TI_GNU_ATTRIBUTE_SUPPORT__)

Darüber hinaus unterstützt MSVC die __declspec(noinline) Rückkehr zu Visual Studio 7.1. Intel unterstützt es wahrscheinlich auch (sie versuchen, sowohl mit GCC als auch mit MSVC kompatibel zu sein), aber ich habe mir nicht die Mühe gemacht, dies zu überprüfen. Die Syntax ist grundsätzlich dieselbe:

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

PGI 10.2+ (und wahrscheinlich älter) unterstützt ein noinlinePragma, das für die nächste Funktion gilt:

#pragma noinline
static void foo(void) { }

TI 6.0+ unterstützt ein FUNC_CANNOT_INLINE Pragma, das (ärgerlicherweise) in C und C ++ unterschiedlich funktioniert. In C ++ ähnelt es den ggA:

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

In C ist jedoch der Funktionsname erforderlich:

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

Cray 6.4+ (und möglicherweise früher) verfolgt einen ähnlichen Ansatz und erfordert den Funktionsnamen:

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

Oracle Developer Studio unterstützt auch ein Pragma, das den Funktionsnamen verwendet und mindestens auf Forte Developer 6 zurückgeht . Beachten Sie jedoch, dass es auch in neueren Versionen nach der Deklaration erfolgen muss:

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

Abhängig davon, wie engagiert Sie sind, könnten Sie ein Makro erstellen, das überall funktioniert, aber Sie müssten den Funktionsnamen sowie die Deklaration als Argumente haben.

Wenn Sie, OTOH, mit etwas einverstanden sind, das nur für die meisten Menschen funktioniert, können Sie mit etwas davonkommen, das etwas ästhetischer ist und bei dem Sie sich nicht wiederholen müssen. Das ist der Ansatz, den ich für Hedley gewählt habe , wo die aktuelle Version von HEDLEY_NEVER_INLINE so aussieht:

#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

Wenn Sie Hedley nicht verwenden möchten (es ist ein einzelner Public Domain / CC0-Header), können Sie die Versionsprüfungsmakros ohne großen Aufwand konvertieren, aber mehr, als ich bereit bin, ☺ einzugeben.

nemequ
quelle
Vielen Dank für den Link zu Ihrem Projekt @nemequ. Ich habe unsere anderen Entwickler gebeten, es für unsere Verwendung zu bewerten. Wir haben verschiedene Architekturen.
Daisuke Aramaki
Es würde mich sehr interessieren, was sie sagen, besonders wenn sie nicht interessiert sind. Und natürlich bin ich da, um Fragen zu beantworten (GitHub Issue Tracker, E-Mail, was auch immer ...).
Nemequ
14

Falls Sie einen Compilerfehler für erhalten __attribute__((noinline)), können Sie einfach versuchen:

noinline int func(int arg)
{
    ....
}
Sam Liao
quelle
10
static __attribute__ ((noinline))  void foo()
{

}

Das hat bei mir funktioniert.

KenBee
quelle
8

Verwenden Sie das noinline Attribut :

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

Sie sollten es wahrscheinlich sowohl verwenden, wenn Sie die Funktion für den externen Gebrauch deklarieren, als auch wenn Sie die Funktion schreiben.

Chris Lutz
quelle
2

Ich arbeite mit gcc 7.2. Ich brauchte speziell eine Funktion, um nicht inline zu sein, weil sie in einer Bibliothek instanziiert werden musste. Ich habe sowohl die __attribute__((noinline))Antwort als auch die asm("")Antwort ausprobiert . Keiner löste das Problem.

Schließlich stellte ich fest, dass das Definieren einer statischen Variablen innerhalb der Funktion den Compiler dazu zwingt, Speicherplatz im statischen Variablenblock zuzuweisen und beim ersten Aufruf der Funktion eine Initialisierung dafür auszugeben.

Dies ist eine Art schmutziger Trick, aber es funktioniert.

Ofri Sadowsky
quelle
Sie können Ihre Funktion inline void foo(void) { ... }in einem Header definieren und extern inline void foo(void);in einer Bibliotheksquelldatei deklarieren . Nach der C99-Semantik kann der Compiler die Funktion einbinden, wenn es ihm gefällt UND Objektcode in Ihrer Bibliothek ausgeben. Siehe Ist "Inline" ohne "statisch" oder "extern" in C99 jemals nützlich? .
Diapir