Wann wird die Inline-Funktion verwendet und wann wird sie nicht verwendet?

184

Ich weiß, dass Inline ein Hinweis oder eine Anforderung an den Compiler ist und zur Vermeidung von Funktionsaufruf-Overheads verwendet wird.

Auf welcher Grundlage kann man also feststellen, ob eine Funktion ein Kandidat für Inlining ist oder nicht? In welchem ​​Fall sollte man Inlining vermeiden?

Ashish
quelle
11
inlineist für den C ++ - Neuling das, was CFLAGSfür den Gentoo-Neuling ist: Nein, das Kompilieren mit -O3 -funroll-loops -finline-functionsbringt Ihren alten Pentium nicht zum Fliegen;)
Gregory Pakosz
1
Ein Grund, Inline nicht zu verwenden, besteht darin, dass Sie bei einigen Debuggern keinen Haltepunkt festlegen oder in eine Inline-Funktion eintreten können.
Rob deFriesse
1
Duplikat: stackoverflow.com/questions/1875947/…
Steve Jessop
5
Sie sollten nicht festlegen, ob eine Funktion inline sein soll oder nicht. Lassen Sie den Compiler es tun; Es ist besser als Sie (und kann Funktionen selektiv basierend auf der Umgebung jedes Anrufs einbinden).
David Thornley
@DavidThornley Manchmal integriert der Compiler die Funktion selbst bei gesetztem O3-Flag nicht, wenn sich die Definition in einer CPP-Datei befindet. Die Faustregel, der ich folge, ist also, einen Liner und auch diese Funktionen ohne Schleifen zu inline.
TalekeDskobeDa

Antworten:

209

Das Vermeiden der Kosten eines Funktionsaufrufs ist nur die halbe Wahrheit.

machen:

  • verwenden inlinestatt#define
  • Sehr kleine Funktionen sind gute Kandidaten für inline: schnelleren Code und kleinere ausführbare Dateien (mehr Chancen, im Code-Cache zu bleiben)
  • Die Funktion ist klein und wird sehr oft aufgerufen

nicht:

  • Große Funktionen: Führt zu größeren ausführbaren Dateien, was die Leistung erheblich beeinträchtigt, unabhängig von der schnelleren Ausführung, die sich aus dem Aufrufaufwand ergibt
  • Inline-Funktionen, die an E / A gebunden sind
  • Die Funktion wird selten verwendet
  • Konstruktoren und Destruktoren: Auch wenn der Compiler leer ist, generiert er Code für sie
  • Unterbrechen der Binärkompatibilität bei der Entwicklung von Bibliotheken:
    • Inline eine vorhandene Funktion
    • Ändern Sie eine Inline-Funktion oder machen Sie eine Inline-Funktion nicht inline: In einer früheren Version der Bibliothek wird die alte Implementierung aufgerufen

Wenn Sie eine Bibliothek entwickeln, sollten Sie Folgendes tun, um eine Klasse in Zukunft erweiterbar zu machen:

  • Fügen Sie einen virtuellen Inline-Destruktor hinzu, auch wenn der Körper leer ist
  • Machen Sie alle Konstruktoren nicht inline
  • Schreiben Sie Nicht-Inline-Implementierungen des Kopierkonstruktors und des Zuweisungsoperators, es sei denn, die Klasse kann nicht nach Wert kopiert werden

Denken Sie daran, dass das inlineSchlüsselwort ein Hinweis für den Compiler ist: Der Compiler kann entscheiden, eine Funktion nicht zu inlineieren, und er kann entscheiden, Funktionen inline zu setzen, die überhaupt nicht markiert wurden inline. Ich vermeide generell Markierungsfunktionen inline(abgesehen davon, wenn ich sehr, sehr kleine Funktionen schreibe).

In Bezug auf die Leistung besteht der kluge Ansatz (wie immer) darin, die Anwendung zu profilieren und schließlich inlineeine Reihe von Funktionen, die einen Engpass darstellen.

Verweise:


EDIT: Bjarne Stroustrup, Die C ++ - Programmiersprache:

Eine Funktion kann als definiert werden inline. Beispielsweise:

inline int fac(int n)
{
  return (n < 2) ? 1 : n * fac(n-1);
}

Der inlineBezeichner ist ein Hinweis für den Compiler, dass er versuchen sollte, Code für einen fac()Inline- Aufruf zu generieren, anstatt den Code für die Funktion einmal festzulegen und dann über den üblichen Funktionsaufrufmechanismus aufzurufen. Ein cleverer Compiler kann die Konstante 720für einen Aufruf generieren fac(6). Die Möglichkeit von gegenseitig rekursiven Inline-Funktionen, Inline-Funktionen, die je nach Eingabe rekursiv sind oder nicht, macht es unmöglich zu garantieren, dass jeder Aufruf einer inlineFunktion tatsächlich inline ist. Der Grad der Klugheit eines Compilers kann nicht gesetzlich festgelegt werden, sodass ein Compiler möglicherweise einen 720anderen 6 * fac(5)und einen weiteren einen nicht inlinierten Aufruf generiert fac(6).

Um Inlining ohne ungewöhnlich clevere Kompilierungs- und Verknüpfungsfunktionen zu ermöglichen, muss die Definition - und nicht nur die Deklaration - einer Inline-Funktion im Geltungsbereich liegen (§9.2). Ein inlineEspecifier hat keinen Einfluss auf die Semantik einer Funktion. Insbesondere hat eine Inline-Funktion immer noch eine eindeutige Adresse, ebenso wie staticVariablen (§7.1.2) einer Inline-Funktion.

EDIT2: ISO-IEC 14882-1998, 7.1.2 Funktionsspezifizierer

Eine Funktionsdeklaration (8.3.5, 9.3, 11.4) mit einem Bezeichner inlinedeklariert eine Inline-Funktion. Der Inline-Spezifizierer gibt der Implementierung an, dass die Inline-Substitution des Funktionskörpers am Aufrufpunkt dem üblichen Funktionsaufrufmechanismus vorzuziehen ist. Eine Implementierung ist nicht erforderlich, um diese Inline-Ersetzung am Aufrufpunkt durchzuführen. Selbst wenn diese Inline-Ersetzung weggelassen wird, bleiben die anderen in 7.1.2 definierten Regeln für Inline-Funktionen ein.

Gregory Pakosz
quelle
34
inlineist viel mehr als ein Hinweis für den Compiler. Es ändert die Sprachregeln für mehrere Definitionen. Statische Daten sind auch kein Grund aus Gusseisen, um das Inlining einer Funktion zu vermeiden. Die Implementierung muss für jede statische Funktion ein einzelnes statisches Objekt zuweisen, unabhängig davon, ob die Funktion deklariert ist inlineoder nicht. Klassen sind weiterhin erweiterbar, wenn sie Inline-Konstruktoren und virtuelle Destruktoren haben. Und ein leerer Klammer-Destruktor ist die einzige virtuelle Funktion, bei der es manchmal eine gute Idee ist, inline zu bleiben.
CB Bailey
2
Es ist ein Hinweis in dem Sinne, dass die Funktion nicht unbedingt inline ist (aber Englisch ist nicht meine Muttersprache). Über die Statik in markierten inlineFunktionen führt dies dazu, dass die Funktion nicht inline wird: Sie zahlen den Preis für den Aufruf, und jede Übersetzungseinheit, die die Funktion enthält und aufruft, erhält eine eigene Kopie des Codes und der statischen Variablen. Der Grund dafür, Konstruktoren und Destruktoren beim Entwickeln einer Bibliothek nicht einzubinden, ist die Binärkompatibilität mit zukünftigen Versionen Ihrer Bibliothek
Gregory Pakosz
14
Es ist ungenau, es als "Hinweis für den Compiler" zu bezeichnen. In der Realität inlinekönnen Nichtfunktionen eingebunden werden, wenn der Compiler dies wünscht. Und inlineFunktionen werden nicht eingebunden, wenn der Compiler beschließt, sie nicht einzubinden. Wie Charles Bailey sagte, ändert es die Sprachregeln. Anstatt es als Optimierungshinweis zu betrachten, ist es genauer, es als ein völlig anderes Konzept zu betrachten. Das inlineSchlüsselwort weist den Compiler an, mehrere Definitionen zuzulassen, und sonst nichts. Die "Inlining" -Optimierung kann auf fast jede Funktion angewendet werden, unabhängig davon, ob sie markiert ist oder nicht inline.
Jalf
26
Es ist nur so, dass ich überrascht bin, wenn Stroustrup schreibt "der Inline-Spezifizierer ist ein Hinweis für den Compiler", dass ich beschuldigt werde, ihn zitiert zu haben. Wie auch immer, ich habe genug Zeit damit verbracht, mein Bestes zu geben, um diese Antwort mit so vielen Referenzen wie möglich zu
untermauern
2
@ GregoryPakosz: Aber wir verwenden nicht alle inline, um Funktionsinlining zu erhalten. Manchmal wollen wir die anderen Vorteile, wie das Umgehen von ODR.
Leichtigkeitsrennen im Orbit
57

inlinehat sehr wenig mit Optimierung zu tun. inlineist eine Anweisung an den Compiler, keinen Fehler zu erzeugen, wenn die definierte Funktion mehrmals im Programm vorkommt, und ein Versprechen, dass die Definition in jeder verwendeten Übersetzung vorkommt und überall dort, wo sie erscheint, genau dieselbe Definition hat.

In Anbetracht der oben genannten Regeln inlineeignet es sich für kurze Funktionen, deren Hauptteil keine zusätzlichen Abhängigkeiten von dem erfordert, was nur eine Deklaration benötigen würde. Jedes Mal, wenn die Definition gefunden wird, muss sie analysiert werden, und es kann Code für ihren Text generiert werden, sodass ein gewisser Compiler-Overhead für eine Funktion entsteht, die nur einmal in einer einzelnen Quelldatei definiert ist.

Ein Compiler kann jeden von ihm gewählten Funktionsaufruf inline (dh einen Aufruf der Funktion durch Code ersetzen, der diese Aktion dieser Funktion ausführt). Früher war es so, dass es "offensichtlich" nicht möglich war, eine Funktion zu integrieren, die nicht in derselben Übersetzungseinheit wie der Aufruf deklariert war, aber mit der zunehmenden Verwendung der Optimierung der Verbindungszeit ist auch dies jetzt nicht mehr der Fall. Ebenso wahr ist die Tatsache, dass markierte Funktionen inlinemöglicherweise nicht inline sind.

CB Bailey
quelle
Ich habe das Gefühl, dass dies eher ein glücklicher Zufall als eine absichtliche Funktion von C ++ ist. Die Idee ist den 'statischen' globalen Variablen von C sehr ähnlich. Es ist jedoch eine sehr interessante Antwort. Ich wünschte, sie hätten nur ein Schlüsselwort wie "intern" verwendet, um die interne Verknüpfung anzuzeigen.
Rehno Lindeque
+1. @Rehno: Ich bin mir nicht sicher, was du sagst. Was hat die Verknüpfung mit dem inlineSchlüsselwort zu tun ? Und was ist ein glücklicher Zufall?
Jalf
@jalf: Wenn ich meinen Kommentar im Nachhinein lese, stelle ich fest, dass er ziemlich vage und nicht so gut durchdacht ist. Das Definieren derselben Funktion in mehreren Dateien führt zu einem Linkerfehler, dem durch Deklarieren der Funktion 'statisch' begegnet werden kann. Mit 'Inline' können Sie jedoch dasselbe mit subtilen Unterschieden tun, die keine interne Verknüpfung erhalten, wie dies bei 'Static' der Fall ist. Ich vermute, dass dies eigentlich eher ein Zufall ist, da Sprachimplementierer / -designer erkannt haben, dass sie mit Funktionen, die in Header-Dateien deklariert und auf 'inline' übertragen wurden, etwas Besonderes tun müssen.
Rehno Lindeque
4
Ich bin mir nicht sicher, warum Ihr Kommentar so viele Stimmen erhalten hat, da die Leistung der Hauptgrund für die Verwendung von Inline ist.
Gast128
10

Dem Compiler zu sagen, dass er eine Funktion einbinden soll, ist eine Optimierung, und die wichtigste Regel für die Optimierung ist, dass vorzeitige Optimierung die Wurzel allen Übels ist. Schreiben Sie immer klaren Code (mit effizienten Algorithmen), profilieren Sie dann Ihr Programm und optimieren Sie nur Funktionen, die zu lange dauern.

Wenn Sie feststellen, dass eine bestimmte Funktion sehr kurz und einfach ist und in einer engen inneren Schleife zehntausende Male aufgerufen wird, ist sie möglicherweise ein guter Kandidat.

Sie könnten jedoch überrascht sein - viele C ++ - Compiler integrieren automatisch kleine Funktionen für Sie - und ignorieren möglicherweise auch Ihre Anfrage nach Inline.

dmazzoni
quelle
In der Tat habe ich den Verdacht, dass bestimmte Compiler 'inline' sehr hinterhältig ignorieren und nur auf '__inline' oder '__force_inline' reagieren. Ich nehme an, das soll Missbrauch verhindern!
Rehno Lindeque
Normalerweise nicht der Fall. Inline ist nur ein Hinweis, aber es ist ein Hinweis, den die meisten Compiler ernst nehmen. Sie können den Compiler so einstellen, dass er die Assemblersprache zusammen mit dem Objektcode ( /FAcsin Visual Studio, -sin GCC) ausgibt, um genau zu sehen, was er tut. Nach meiner Erfahrung wiegen beide Compiler das Inline-Keyword ziemlich stark.
Crashworks
1
Es ist interessant, weil meiner Erfahrung nach weder g ++ noch VC das inlineSchlüsselwort wiegen . Das heißt, wenn Sie sehen, dass die Funktion inline ist, und den Bezeichner daraus entfernen inline, wird sie weiterhin inline. Wenn Sie konkrete Beispiele für das Gegenteil haben, teilen Sie diese bitte mit!
Pavel Minaev
4
Wie verhindert das inlineSchlüsselwort "Code löschen"? Das Schlüsselwort in "vorzeitige Optimierung" ist verfrüht , nicht Optimierung. Zu sagen, dass Sie Optimierungen aktiv vermeiden sollten, ist nur Quatsch. Der Punkt dieses Zitats ist, dass Sie die Optimierungen vermeiden sollten, die möglicherweise nicht notwendig sind, und schädliche Nebenwirkungen auf den Code haben sollten (z. B. ihn weniger wartbar machen). Ich kann nicht erkennen, wie das inlineSchlüsselwort den Code weniger wartbar macht oder wie schädlich es sein kann, ihn einer Funktion hinzuzufügen.
Jalf
3
jalf, manchmal macht das Inlinen einer Funktion Ihren Code langsamer, nicht schneller. Ein Beispiel ist, wenn die Funktion von mehreren verschiedenen Stellen in Ihrem Code aufgerufen wird. Wenn die Funktion nicht inline ist, befindet sie sich möglicherweise noch im Anweisungscache, wenn sie von einer anderen Stelle aufgerufen wird, und der Verzweigungsprädiktor ist möglicherweise bereits aufgewärmt. Es gibt einige Muster, die die Effizienz immer verbessern, daher schadet es nie, sie zu verwenden. Inlining gehört nicht dazu. Es hat normalerweise überhaupt keinen Einfluss auf die Leistung, manchmal hilft es und manchmal tut es weh. Ich stehe hinter meinem Rat: Profil zuerst, dann Inline.
Dmazzoni
5

Der beste Weg, dies herauszufinden, besteht darin, Ihr Programm zu profilieren und kleine Funktionen zu markieren, die häufig aufgerufen werden, und CPU-Zyklen durchzubrennen, die als inline. Das Schlüsselwort hier ist "klein" - sobald der Overhead des Funktionsaufrufs im Vergleich zu der in der Funktion verbrachten Zeit vernachlässigbar ist, ist es sinnlos, sie zu integrieren.

Die andere Verwendung, die ich vorschlagen würde, ist, wenn Sie kleine Funktionen haben, die im leistungskritischen Code oft genug aufgerufen werden, um einen Cache-Miss relevant zu machen, sollten Sie diese wahrscheinlich auch inline setzen. Auch dies sollte der Profiler Ihnen sagen können.

Timo Geusch
quelle
4

Vorzeitige Optimierung ist die Wurzel allen Übels!

Als Faustregel habe ich normalerweise nur "Getter" und "Setter" inline. Sobald der Code funktioniert und stabil ist, kann die Profilerstellung zeigen, welche Funktionen vom Inlining profitieren können.

Auf der anderen Seite verfügen die meisten modernen Compiler über recht gute Optimierungsalgorithmen und geben an, was Sie für Sie hätten tun sollen.

Wiederaufnahme - Schreiben Sie Inline-Einzeilerfunktionen und sorgen Sie sich später um andere.

Kornel Kisielewicz
quelle
2

Inline-Funktionen können die Codeleistung verbessern, da keine Argumente mehr in den Stapel verschoben werden müssen. Wenn sich die betreffende Funktion in einem kritischen Teil Ihres Codes befindet, sollten Sie die Inline- und nicht Inline-Entscheidung im Optimierungsteil Ihres Projekts treffen.

Weitere Informationen zu Inlines finden Sie in der FA ++ - FAQ

Alon
quelle
1

Ich benutze Inline-Funktionen oft nicht als Optimierung, sondern um den Code besser lesbar zu machen. Manchmal ist der Code selbst kürzer und leichter zu verstehen als Kommentare, beschreibende Namen usw. Zum Beispiel:

void IncreaseCount() { freeInstancesCnt++; }

Der Leser kennt sofort die vollständige Semantik des Codes.

danatel
quelle
0

Ich folge im Allgemeinen einer Daumenregel, bei der ich eine Funktion mit 3-4 einfachen Anweisungen als Inline mache. Es ist jedoch gut daran zu denken, dass dies nur ein Hinweis für den Compiler ist. Der letzte Aufruf, um es inline zu machen oder nicht, wird nur vom Compiler ausgeführt. Wenn es mehr als diese vielen Anweisungen gibt, werde ich nicht inline deklarieren, da dies bei einem dummen Compiler zu einem Aufblähen des Codes führen kann.

Naveen
quelle
0

Der beste Weg wäre, die generierten Anweisungen für Inline und nicht Inline zu untersuchen und zu vergleichen. Es ist jedoch immer sicher, wegzulassen inline. Die Verwendung inlinekann zu Problemen führen, die Sie nicht möchten.

Wallyk
quelle
0

Bei der Entscheidung, ob Inline verwendet werden soll, denke ich normalerweise an die folgende Idee: Auf modernen Maschinen kann die Speicherlatenz ein größerer Engpass sein als bei Rohberechnungen. Es ist bekannt, dass Inlining-Funktionen, die häufig aufgerufen werden, die Größe der ausführbaren Datei erhöhen. Darüber hinaus könnte eine solche Funktion im Code-Cache der CPU gespeichert werden, wodurch die Anzahl der Cache-Fehler verringert wird, wenn auf diesen Code zugegriffen werden muss.

Daher müssen Sie selbst entscheiden: Erhöht oder verringert Inlining die Größe des generierten Maschinencodes? Wie wahrscheinlich ist es, dass das Aufrufen der Funktion einen Cache-Fehler verursacht? Wenn es im gesamten Code verteilt ist, würde ich sagen, dass die Wahrscheinlichkeit hoch ist. Wenn es auf eine einzelne enge Schleife beschränkt ist, ist die Wahrscheinlichkeit hoffentlich gering.

Ich verwende normalerweise Inlining in den Fällen, in denen ich unten aufgeführt bin. Wenn Sie jedoch ernsthaft über die Leistung besorgt sind, ist die Profilerstellung unerlässlich. Außerdem möchten Sie möglicherweise überprüfen, ob der Compiler den Hinweis tatsächlich versteht.

  • Kurze Routinen, die in einer engen Schleife aufgerufen werden.
  • Sehr einfache Accessoren (get / set) und Wrapper-Funktionen.
  • Vorlagencode in Header-Dateien erhält leider automatisch den Inline-Hinweis.
  • Funktionscode, der wie ein Makro verwendet wird. (ZB min () / max ())
  • Kurze mathematische Routinen.
Rehno Lindeque
quelle
0

Eine Inline-Methode hat auch schwerwiegende Nebenwirkungen bei der Verwaltung großer Projekte. Wenn der Inline-Code geändert wird, werden alle Dateien, die ihn verwenden, vom Compiler automatisch neu erstellt (es ist ein guter Compiler). Dies könnte viel Entwicklungszeit verschwenden.

Wenn eine inlineMethode in eine Quelldatei übertragen und nicht mehr eingebunden wird, muss das gesamte Projekt neu erstellt werden (zumindest war dies meine Erfahrung). Und auch, wenn Methoden in Inline konvertiert werden.

Thomas Matthews
quelle
1
Das ist ein anderes Thema. Sie erhalten das Wiederherstellungsproblem für Code, der in einer Header-Datei abgelegt ist. Ob es markiert ist inlineoder nicht, spielt keine Rolle (außer ohne das inlineSchlüsselwort werden Linker-Fehler inline
angezeigt
Das Ändern einer Inline-Methode führt jedoch zu übermäßigen Builds im Vergleich zum Ändern einer Nicht-Inline-Methode in einer Quelldatei.
Thomas Matthews
0

Das Inline- Funktionsqualifikationsmerkmal sollte nur verwendet werden, wenn der Funktionscode klein ist. Wenn die Funktionen größer sind, sollten Sie die normalen Funktionen bevorzugen, da die Einsparung von Speicherplatz das vergleichsweise geringe Opfer an Ausführungsgeschwindigkeit wert ist.

nitish
quelle
0

Wenn Sie der Meinung sind, dass Ihr Code klein genug ist, um als Inline-Funktion verwendet zu werden, und sich daran erinnern, dass die Inline-Funktion Ihren Code dupliziert und dort einfügt, wo die Funktion aufgerufen wird, ist sie möglicherweise gut genug, um die Ausführungszeit zu erhöhen, erhöht aber auch den Speicherverbrauch. Sie können die Inline-Funktion nicht verwenden, wenn Sie eine Funktion loop / static variable / recursive / switch / goto / Virtual verwenden. Virtuelle Mittel warten bis zur Laufzeit und Inline bedeutet während der Kompilierung, sodass sie nicht gleichzeitig verwendet werden können.

Sachin Pathak
quelle
-2

Ich habe einige Antworten gelesen und sehe, dass einige Dinge fehlen.

Die Regel, die ich verwende, ist, Inline nicht zu verwenden, es sei denn, ich möchte, dass es Inline ist. Sieht albern aus, jetzt Erklärung.

Compiler sind intelligent genug und kurze Funktionen machen immer Inline. Und macht niemals lange Funktion als Inline, es sei denn, der Programmierer hat dies gesagt.

Ich weiß, dass Inline ein Hinweis oder eine Anfrage an den Compiler ist

Eigentlich inlineist eine Bestellung für Compiler, es hat keine Auswahl und nach inlineSchlüsselwort macht der gesamte Code inline. Sie können also niemals ein inlineSchlüsselwort verwenden und der Compiler entwirft den kürzesten Code.

Also wann verwenden inline?

Zu verwenden, wenn Sie Code inline haben möchten. Ich kenne nur ein Beispiel, weil ich es nur in einer Situation verwende. Es ist eine Benutzerauthentifizierung.

Zum Beispiel habe ich diese Funktion:

inline bool ValidUser(const std::string& username, const std::string& password)
{
    //here it is quite long function
}

Egal wie groß diese Funktion ist, ich möchte sie als Inline haben, da meine Software dadurch schwerer zu knacken ist.

ST3
quelle
2
Inline ist immer noch ein Hinweis. Der Compiler kann möglicherweise nicht inline arbeiten, wenn er der Ansicht ist, dass Ihre Funktion zu aufgebläht ist.
Pete
Einer sagt, Inline ist ein Befehl ... der andere sagt, es ist ein Hinweis. Würde jemand seine Aussage begründen, damit wir feststellen können, welche wahr ist?
@ user2918461 Ich unterstütze die Aussage Inline ist nur ein Hinweis. Dies wurde von vielen Websites und Büchern unterstützt
WARhead