Wann sollte ich das Schlüsselwort inline
für eine Funktion / Methode in C ++ schreiben ?
Nachdem Sie einige Antworten gesehen haben, einige verwandte Fragen:
Wann sollte ich das Schlüsselwort 'inline' für eine Funktion / Methode in C ++ nicht schreiben?
Wann weiß der Compiler nicht, wann eine Funktion / Methode 'inline' gemacht werden soll?
Ist es wichtig, ob eine Anwendung Multithreading ist, wenn man 'Inline' für eine Funktion / Methode schreibt?
c++
inline
one-definition-rule
Teilweise
quelle
quelle
inline
(9.3 / 2).Antworten:
Oh Mann, einer meiner Lieblingstiere.
inline
ist mehr wiestatic
oderextern
als eine Richtlinie , den Compiler zu sagen Ihre Funktionen inline.extern
,static
,inline
Sind Verknüpfung Richtlinien, fast ausschließlich durch den Linker verwendet wird , nicht der Compiler.Es wird gesagt, dass
inline
der Compiler darauf hinweist, dass Sie der Meinung sind, dass die Funktion inline sein sollte. Das mag 1998 wahr gewesen sein, aber ein Jahrzehnt später braucht der Compiler keine solchen Hinweise. Ganz zu schweigen davon, dass Menschen normalerweise falsch liegen, wenn es um die Optimierung von Code geht. Daher ignorieren die meisten Compiler den „Hinweis“.static
- Der Name der Variablen / Funktion kann nicht in anderen Übersetzungseinheiten verwendet werden. Linker muss sicherstellen, dass nicht versehentlich eine statisch definierte Variable / Funktion einer anderen Übersetzungseinheit verwendet wird.extern
- Verwenden Sie diesen Variablen- / Funktionsnamen in dieser Übersetzungseinheit, aber beschweren Sie sich nicht, wenn er nicht definiert ist. Der Linker sortiert es und stellt sicher, dass der gesamte Code, der versucht hat, ein externes Symbol zu verwenden, seine Adresse hat.inline
- Diese Funktion wird in mehreren Übersetzungseinheiten definiert. Machen Sie sich keine Sorgen. Der Linker muss sicherstellen, dass alle Übersetzungseinheiten eine einzelne Instanz der Variablen / Funktion verwenden.Hinweis: Im Allgemeinen ist das Deklarieren von Vorlagen
inline
sinnlos, da sie bereits die Verknüpfungssemantik von habeninline
. Allerdings explizite Spezialisierung und Instanziierung von Templates benötigeninline
verwendet werden.Spezifische Antworten auf Ihre Fragen:
Nur wenn Sie möchten, dass die Funktion in einem Header definiert wird. Genauer gesagt nur, wenn die Definition der Funktion in mehreren Übersetzungseinheiten angezeigt werden kann. Es ist eine gute Idee, kleine (wie in einem Liner) Funktionen in der Header-Datei zu definieren, da der Compiler mehr Informationen erhält, mit denen er arbeiten kann, während er Ihren Code optimiert. Es erhöht auch die Kompilierungszeit.
Fügen Sie keine Inline hinzu, nur weil Sie glauben, dass Ihr Code schneller ausgeführt wird, wenn der Compiler ihn inline macht.
Im Allgemeinen kann der Compiler dies besser als Sie. Der Compiler hat jedoch nicht die Möglichkeit, Code zu integrieren, wenn er nicht über die Funktionsdefinition verfügt. In maximal optimiertem Code werden normalerweise alle
private
Methoden eingefügt, unabhängig davon, ob Sie danach fragen oder nicht.Um Inlining in GCC zu verhindern, verwenden Sie
__attribute__(( noinline ))
und in Visual Studio__declspec(noinline)
.Multithreading hat keinerlei Auswirkungen auf das Inlining.
quelle
inline
Schlüsselwort nicht zusammenhängen. Du hast aber die richtige Idee. Das Erraten, was durch Inlining verbessert werden würde, ist in der Regel sehr fehleranfällig. Die Ausnahme von dieser Regel sind Einzeiler.Ich möchte mit einem überzeugenden Beispiel zu all den großartigen Antworten in diesem Thread beitragen, um verbleibende Missverständnisse zu zerstreuen.
Gegeben zwei Quelldateien, wie:
inline111.cpp:
inline222.cpp:
Fall A:
Kompilieren :
Ausgabe :
Diskussion :
Selbst wenn Sie identische Definitionen Ihrer Inline-Funktionen haben sollten, kennzeichnet der C ++ - Compiler diese nicht, wenn dies nicht der Fall ist (aufgrund der separaten Kompilierung gibt es keine Möglichkeit, dies zu überprüfen). Es ist Ihre eigene Pflicht, dies sicherzustellen!
Linker beschwert sich nicht über One Definition Rule , wie
fun()
als deklariertinline
. Da inline111.cpp jedoch die erstefun()
vom Compiler verarbeitete Übersetzungseinheit ist (die tatsächlich aufruft ), instanziiert der Compilerfun()
bei seiner ersten Aufruf-Begegnung in inline111.cpp . Wenn Compiler entscheidet , nicht zu erweiternfun()
von anderswo in Ihrem Programm auf seine Forderung ( zB von inline222.cpp ), der Aufruf anfun()
wird immer wieder in seine Instanz von erzeugt verknüpft wird inline111.cpp (Aufruf vonfun()
innen inline222.cppkann auch eine Instanz in dieser Übersetzungseinheit erzeugen, bleibt jedoch nicht verbunden). Dies geht aus den identischen&fun = 0x4029a0
Ausdrucken hervor.Schließlich trotz der
inline
Vorschlag an den Compiler zu tatsächlich erweitern die Einzeilerfun()
, es ignoriert Ihren Vorschlag vollständig, was da klar ist ,fun() = 111
in beiden Leitungen.Fall B:
Kompilieren (umgekehrte Reihenfolge beachten) :
Ausgabe :
Diskussion :
Dieser Fall bestätigt, was in Fall A besprochen wurde .
Beachten Sie einen wichtigen Punkt: Wenn Sie den tatsächlichen Aufruf von
fun()
in inline222.cpp auskommentieren ( z. B. die Kommentarausgabecout
in inline222.cpp vollständig auskommentieren ), wird er trotz der Kompilierungsreihenfolge Ihrer Übersetzungseinheitenfun()
beim ersten Aufruf in instanziiert inline111.cpp , was zum Ausdruck für Fall B als führtinline111: fun() = 111, &fun = 0x402980
.Fall C:
Kompilieren (Hinweis -O2) :
oder
Ausgabe :
Diskussion :
-O2
ermutigt die Optimierung den Compiler , die Funktionen, die eingebunden werden können , tatsächlich zu erweitern (Beachten Sie auch, dass dies ohne Optimierungsoptionen Standard-fno-inline
ist ). Wie aus dem Ausdruck hier hervorgeht, wurde der tatsächlich inline erweitert (gemäß seiner Definition in dieser bestimmten Übersetzungseinheit), was zu zwei verschiedenen Ausdrucken führte. Trotzdem gibt es immer noch nur eine global verknüpfte Instanz von (wie vom Standard gefordert), wie aus identischen Ausdrucken hervorgeht.fun()
fun()
fun()
&fun
quelle
inline
Funktionen zu undefiniertem Verhalten macht..cpp
eine eigene Übersetzungseinheit ist. Fügen Sie vorzugsweise Fälle für-flto
aktiviert / deaktiviert hinzu.Sie müssen Ihre Funktion bei der Vorlagenspezialisierung weiterhin explizit einbinden (wenn sich die Spezialisierung in der .h-Datei befindet).
quelle
1) Heutzutage so ziemlich nie. Wenn es eine gute Idee ist, eine Funktion zu integrieren, wird der Compiler dies ohne Ihre Hilfe tun.
2) Immer. Siehe Nr. 1.
(Bearbeitet, um zu reflektieren, dass Sie Ihre Frage in zwei Fragen unterteilt haben ...)
quelle
inline
wird weiterhin benötigt, um beispielsweise eine Funktion in einer Header-Datei zu definieren (und dies ist erforderlich, um eine solche Funktion in mehrere Kompilierungseinheiten zu integrieren).inline
einen Bezeichner verfügt , werden ihre Instanzen vom Linker automatisch zu einer Instanz zusammengefasst, und ODR wird nicht verwendet.Wenn die Funktion im Header deklariert und in der
.cpp
Datei definiert ist , sollten Sie dies nicht tun das Schlüsselwort schreiben.Es gibt keine solche Situation. Der Compiler kann keine Funktion inline erstellen. Alles, was es tun kann, ist, einige oder alle Aufrufe der Funktion einzubinden. Dies ist nicht möglich, wenn der Code der Funktion nicht vorhanden ist (in diesem Fall muss der Linker dies tun, wenn er dazu in der Lage ist).
Nein, das spielt überhaupt keine Rolle.
quelle
Dies hängt vom verwendeten Compiler ab. Vertrauen Sie nicht blind darauf, dass Compiler heutzutage besser als Menschen wissen, wie man inline ist, und Sie sollten es aus Leistungsgründen niemals verwenden, da es sich eher um eine Verknüpfungsrichtlinie als um einen Optimierungshinweis handelt. Ich stimme zwar zu, dass diese Argumente ideologisch korrekt sind, aber die Begegnung mit der Realität könnte etwas anderes sein.
Nachdem ich mehrere Threads gelesen hatte, versuchte ich aus Neugier die Auswirkungen von Inline auf den Code, an dem ich gerade arbeite, und das Ergebnis war, dass ich eine messbare Beschleunigung für GCC und keine Beschleunigung für Intel Compiler erhielt.
(Weitere Details: Mathematische Simulationen mit wenigen außerhalb der Klasse definierten kritischen Funktionen, GCC 4.6.3 (g ++ -O3), ICC 13.1.0 (icpc -O3); Hinzufügen von Inline zu kritischen Punkten verursachte + 6% Beschleunigung mit GCC-Code).
Wenn Sie also GCC 4.6 als modernen Compiler qualifizieren, ist die Inline-Direktive immer noch wichtig, wenn Sie CPU-intensive Aufgaben schreiben und wissen, wo genau der Engpass liegt.
quelle
In Wirklichkeit so ziemlich nie. Alles, was Sie tun, ist vorzuschlagen, dass der Compiler eine bestimmte Funktion inline macht (z. B. alle Aufrufe dieser Funktion / mit ihrem Hauptteil ersetzen). Es gibt natürlich keine Garantien: Der Compiler kann die Direktive ignorieren.
Der Compiler kann solche Dinge im Allgemeinen gut erkennen und optimieren.
quelle
inline
es in C ++ einen semantischen Unterschied gibt (z. B. in der Art und Weise, wie mehrere Definitionen behandelt werden), was in einigen Fällen wichtig ist (z. B. Vorlagen).Ich habe dies für Visual Studio 9 (15.00.30729.01) überprüft, indem ich mit / FAcs kompiliert und den Assembly-Code überprüft habe: Der Compiler hat Aufrufe an Mitgliedsfunktionen ohne aktivierte Optimierung erstellt Debug- Modus . Selbst wenn die Funktion mit __forceinline markiert ist , wird kein Inline-Laufzeitcode erzeugt.
quelle
Sie möchten es ganz am Anfang setzen, bevor Sie den Rückgabetyp eingeben. Aber die meisten Compiler ignorieren es. Wenn es definiert ist und einen kleineren Codeblock enthält, betrachten es die meisten Compiler ohnehin als Inline.
quelle
Sofern Sie keine Bibliothek schreiben oder besondere Gründe haben, können Sie die Optimierung der Verbindungszeit vergessen
inline
und stattdessen verwenden. Es entfällt die Anforderung, dass sich eine Funktionsdefinition in einem Header befinden muss, damit sie für das Inlining über Kompilierungseinheiten hinweg berücksichtigt werden kann. Genau das ist esinline
ermöglicht.(Aber siehe Gibt es einen Grund, die Verbindungszeitoptimierung nicht zu verwenden? )
quelle
In der Reihe Schlüsselwort fordert den Compiler auf, den Funktionsaufruf durch den Hauptteil der Funktion zu ersetzen. Er wertet zuerst den Ausdruck aus und übergibt ihn dann. Er reduziert den Funktionsaufruf-Overhead, da die Rücksprungadresse nicht gespeichert werden muss und für die Funktion kein Stapelspeicher erforderlich ist Argumente.
Wann zu verwenden:
quelle
inline
in C und C ++ verwenden oder nicht. C Inline: stackoverflow.com/a/62287072/7194773 C ++ inline: stackoverflow.com/a/62230963/7194773C ++ Inline unterscheidet sich grundlegend von C Inline .
inline
allein wirkt sich auf den Compiler, Assembler und den Linker aus. Es ist eine Anweisung an den Compiler, dass nur dann ein Symbol für diese Funktion / Daten ausgegeben wird, wenn es in der Übersetzungseinheit verwendet wird, und wenn dies der Fall ist, weisen Sie den Assembler wie Klassenmethoden an, sie im Abschnitt.section .text.c::function(),"axG",@progbits,c::function(),comdat
oder.section .bss.i,"awG",@nobits,i,comdat
für Daten zu speichern .Dies folgt
.section name, "flags"MG, @type, entsize, GroupName[, linkage]
. Der Abschnittsname lautet beispielsweise.text.c::function()
.axG
bedeutet, dass der Abschnitt zuweisbar, ausführbar und in einer Gruppe ist, dh ein Gruppenname wird angegeben (und es gibt kein M-Flag, sodass keine Entsize angegeben wird);@progbits
bedeutet, dass der Abschnitt Daten enthält und nicht leer ist; Verknüpfung bedeutet, dass in allen Objektdateien alle Abschnitte mit diesem mit comdat gekennzeichneten Gruppennamen aus der endgültigen ausführbaren Datei entfernt werden, mit Ausnahme von 1, dh der Compiler stellt sicher, dass nur eine Definition in der Übersetzungseinheit vorhanden ist, und weist den Assembler an, diese zu setzen es in seiner eigenen Gruppe in der Objektdatei (1 Abschnitt in 1 Gruppe) und dann stellt der Linker sicher, dass, wenn Objektdateien eine Gruppe mit demselben Namen haben, nur eine in die endgültige EXE-Datei aufgenommen wird. Der Unterschied zwischenc::function()
ist der Gruppenname und die Gruppe hatcomdat
inline
und nicht verwendeninline
ist jetzt für den Assembler und als Ergebnis für den Linker sichtbar, da es nicht im regulären.data
oder gespeichert ist.text
vom Assembler aufgrund ihrer Anweisungen usw. .static inline
in einer Klasse bedeutet dies, dass es sich um eine Typdefinition und nicht um eine Deklaration handelt (ermöglicht die Definition eines statischen Elements in der Klasse) und macht es inline; es verhält sich jetzt wie oben.static inline
at file scope wirkt sich nur auf den Compiler aus. Für den Compiler bedeutet dies: Geben Sie nur dann ein Symbol für diese Funktion / Daten aus, wenn es in der Übersetzungseinheit verwendet wird, und zwar als reguläres statisches Symbol (speichern Sie in.text /.data ohne die Anweisung .globl). Für den Assembler gibt es jetzt keinen Unterschied zwischenstatic
undstatic inline
extern inline
ist eine Deklaration, die bedeutet, dass Sie dieses Symbol in der Übersetzungseinheit definieren oder einen Compilerfehler auslösen müssen. Wenn es definiert ist, behandeln Sie es als regulärinline
und für den Assembler und Linker gibt es keinen Unterschied zwischenextern inline
undinline
, daher ist dies nur ein Compiler-Schutz.Das Ganze oben ohne die Fehlerzeile kollabiert zu
inline int i[5]
. Natürlich, wenn du esextern inline int i[] = {5};
damals getan hastextern
würden die explizite Definition durch Zuordnung ignoriert fällig werden.inline
Sehen Sie dies und das in einem Namespacequelle
Lassen Sie beim Entwickeln und Debuggen von Code weg
inline
. Dies erschwert das Debuggen.Der Hauptgrund für das Hinzufügen besteht darin, den generierten Code zu optimieren. In der Regel wird dadurch mehr Code-Speicherplatz gegen Geschwindigkeit eingetauscht, manchmal wird jedoch
inline
sowohl Code-Speicherplatz als auch Ausführungszeit gespart.Diese Art von Überlegungen zur Leistungsoptimierung vor Abschluss des Algorithmus sind eine vorzeitige Optimierung .
quelle
inline
Funktionen werden normalerweise nur dann inline geschrieben, wenn sie mit Optimierungen kompiliert wurden. Sie wirken sich also in keiner Weise auf das Debuggen aus. Denken Sie daran, dass es ein Hinweis ist, keine Forderung.inline
Funktionen wurden eingebunden. Es war unmöglich, einen sinnvollen Haltepunkt in ihnen festzulegen.inline
trägt nicht dazu bei, den Code eines modernen Compilers zu verbessern, der selbst herausfinden kann, ob er inline ist oder nicht.Wann sollte man inline:
1.Wenn man den Overhead von Dingen vermeiden möchte, die auftreten, wenn Funktionen wie Parameterübergabe, Steuerübertragung, Steuerungsrückgabe usw. aufgerufen werden.
2.Die Funktion sollte klein sein, häufig aufgerufen werden, und das Inline-Erstellen ist sehr vorteilhaft, da gemäß der 80-20-Regel versucht wird, die Funktion inline zu machen, was einen großen Einfluss auf die Programmleistung hat.
Wie wir wissen, ist Inline nur eine Anforderung an den Compiler, die der Registrierung ähnelt, und kostet Sie bei der Größe des Objektcodes.
quelle
inline
hat seinen Status als Optimierungshinweis verloren, und die meisten Compiler verwenden ihn nur, um mehrere Definitionen zu berücksichtigen - wie es IMO sollte. Mehr noch, seit C ++ 11register
wegen seiner früheren Bedeutung von "Ich weiß besser als der Compiler, wie man optimiert" völlig veraltet ist: Es ist jetzt nur ein reserviertes Wort ohne aktuelle Bedeutung.inline
zu einem gewissen Grad zu.Die C ++ - Inline-Funktion ist ein leistungsstarkes Konzept, das häufig für Klassen verwendet wird. Wenn eine Funktion inline ist, platziert der Compiler an jedem Punkt, an dem die Funktion zur Kompilierungszeit aufgerufen wird, eine Kopie des Codes dieser Funktion.
Bei jeder Änderung an einer Inline-Funktion müssen möglicherweise alle Clients der Funktion neu kompiliert werden, da der Compiler den gesamten Code erneut ersetzen müsste, da sonst die alte Funktionalität beibehalten wird.
Um eine Funktion inline zu setzen, setzen Sie das Schlüsselwort inline vor den Funktionsnamen und definieren Sie die Funktion, bevor die Funktion aufgerufen wird. Der Compiler kann das Inline-Qualifikationsmerkmal ignorieren, wenn die definierte Funktion mehr als eine Zeile ist.
Eine Funktionsdefinition in einer Klassendefinition ist eine Inline-Funktionsdefinition, auch ohne Verwendung des Inline-Spezifizierers.
Das folgende Beispiel zeigt anhand der Inline-Funktion maximal zwei Zahlen
Weitere Informationen finden Sie hier .
quelle