Hallo, liebes Expertenforum.
https://www.cplusplus.com/articles/2LywvCM9/
"The inline functions are a C++ enhancement feature to increase the
execution time of a program."
Wozu bitte sollte man sich noch extra Mühe geben, damit das Programm
langsamer läuft?
Aus dem Kontext wird doch eindeutig klar, dass der Autor an der Stelle
eher "improve" schreiben wollte.
Vermute dass Englisch nicht seine Muttersprache ist, der weitere Text
liest sich auch holprig.
Definitiv das falsche Wort. Inline wird benutzt, wenn man an bestimmten
Stellen dem Compiler die "Freiheit" nehmen will, in eine ungewollte
Richtung zu optimieren. Dazu sollte man aber schon wissen was man tut.
Häufig sind Compiler schon recht effizient. Wenn man wirklich schnellen
Code haben möchte, ist es manchmal ganz empfehlenswert, sich den
Assembler/Maschinencode anzusehen, die der Compiler erstellt. Da reicht
oft, in C/C++ den Code etwas anders zu formulieren um sehr viel
schnelleren Code zu bekommen.
Danke für die Antworten. Ja, das ist anscheinend ein Fehler.
"What is inline function :" sollte wohl eher "What is an inline
function: " heißen und "The inline functions are" "Inline functions
are". Daran hätte man das etwas holprige Englisch schon erkennen können.
Andererseits wird dieser Text in einer Box direkt oben auf der
Ergebnisseite der Google-Suche als Kurzinfo angezeigt (alternierend mit
ein oder zwei anderen) und wurde zuletzt 2014 geändert ...
Zur Sache selbst: Inzwischen habe ich mir diese FAQ durchgelesen:
https://isocpp.org/wiki/faq/inline-functions
Wie ihr bereits angedeutet habt, ist das alles wirklich ein wenig vage
(muss es aber wohl auch sein, denn ob das "Inlinen" einer Funktion
sinnvoll ist, hängt natürlich auch von der Zielplattform ab).
Die Bedeutung von inline als Optimierungshinweis (Funktion sollte in die
aufrufende Funktion ausgerollt werden) ist obsolet, der Compiler
entscheidet das heutzutage selber und braucht den Hinweis nicht.
Wichtig ist aber die zweite Bedeutung: Eine als inline definierte
Funktion kann in mehreren translation units definiert werden - d.h. wenn
ich eine inline Funktion f() in einer Headerdatei definiere und diese
von N Sourcen inkludiere, dann habe ich nicht N Defintionen von f() (was
einen Linkerfehler geben würde), sondern nur eine einzige und zwar in
genau dem Sinne, dass die Adresse von f() überall im Programm die
gleiche ist -- was nicht der Fall wäre, wenn man f() static definieren
würde.
Dussel schrieb:> Soweit ich weiß, ist inline sowieso nur eine Empfehlung an den Compiler.
So ist es. Absolut hassenswert. Nö, der Compiler weiß halt nicht in
jedem Fall, ob Inlining effizienter (im Sinn der Gesamtanwendung) wäre,
tut aber so als wüßte er es und handelt entsprechend.
Ganz klar ein NoGo. Unbrauchbares Werkzeug.
Luther B. schrieb:> Wichtig ist aber die zweite Bedeutung
Das ist richtig und der Name Inline ist tatsächlich irreführend.
Bei Templates, die meistens im Header definiert sind ist das Inline
implizit dabei.
c-hater schrieb:> Nö, der Compiler weiß halt nicht in jedem Fall, ob Inlining effizienter> (im Sinn der Gesamtanwendung) wäre, tut aber so als wüßte er es und> handelt entsprechend.
Im allgemeinen weiß er dass nicht, aber bei größeren Anwendungen weiß es
der Entwickler auch nicht.
Wenn man die Performance misst, dann kann man es optimieren. Bei
Compilern nennt sich das Profile-guided optimization.
Meines Wissens ist das "inlining" vom Inline als veraltet zu betrachten.
Compiler ignorieren diesen Hinweis meist eh.
Was das Keyword tatsächlich tut, ist die Linkage zu ändern. Du kannst
"inline int x() {return 0;}" in einen Header schreiben und den in fünf
translation units includen, aber wenn du das inline weglässt, gibt es
ein "multiple definition of". Das ist auch der einzige Zweck, zu dem ich
es heutzutage noch verwenden würde: für inline in Headern implementierte
freie Funktionen.
M.K. B. schrieb:> Luther B. schrieb:>> Wichtig ist aber die zweite Bedeutung>> Das ist richtig und der Name Inline ist tatsächlich irreführend.
Es wird halt dann benötigt, wenn man inlining ermöglichen möchte, indem
man Funktionen im Header definiert. Wenn die Implementation außerhalb
des Headers in einem .cpp-File steht, kann sie in einem anderen nicht
inline expandiert werden, da der Compiler den Code an der Stelle nicht
kennt. Wenn man sie aber in den Header reinschreibt, hat man in jeder
Übersetzungseinheit, in der dieser eingebunden ist, eine eigene
Definition der Funktion, was ohne "inline" nicht erlaubt ist.
In Zeiten von LTO ist das aber auch nicht mehr unbedingt nötig.
Rolf M. schrieb:> Es wird halt dann benötigt, wenn man inlining ermöglichen möchte, indem> man Funktionen im Header definiert. Wenn die Implementation außerhalb> des Headers in einem .cpp-File steht, kann sie in einem anderen nicht> inline expandiert werden, da der Compiler den Code an der Stelle nicht> kennt.
Das war mal ein Problem. Seit LTO klappt das auch mit Code im cpp-File.
c-hater schrieb:> Ganz klar ein NoGo. Unbrauchbares Werkzeug.
Je nun, für diejenigen, die wissen, was sie tun, hat jeder Compiler auch
noch einen Befehl zum zwangsweisen inlining parat. Das solltest du aber
einheitlich auch wissen.
Oliver
Oliver S. schrieb:> Das war mal ein Problem. Seit LTO klappt das auch mit Code im cpp-File.
Hättest meinen Text bis zum Ende lesen sollen :)
Rolf M. schrieb:> In Zeiten von LTO ist das aber auch nicht mehr unbedingt nötig.
Ich halte inline für verdammt wichtig, wenn man Programmabschnitte hat,
bei denen die Laufzeit wichtig ist. Wenn dieser Programmabschnitt aus
vielen verschiedenen Funktionen besteht, die man zur übersichtlichen
Strukturierung angelegt hat, dann will man nicht dass der Überbau der
Funktionsaufrufe in den Maschinenkode kommt. Also macht man alle
Funktionen inline damit der Compiler auch wirklich aus allen Funktionen
eine einzelne erzeugt.
inliner schrieb:> Also macht man alle> Funktionen inline damit der Compiler auch wirklich aus allen Funktionen> eine einzelne erzeugt.
Genau das tut "inline" aber nicht, wie in diesem Thread bereits richtig
angemerkt wurde.
inliner schrieb:> Also macht man alle> Funktionen inline damit der Compiler auch wirklich aus allen Funktionen> eine einzelne erzeugt.
Wie immer gilt: Überlass' das mikro-optimieren dem Compiler.
Oft wird Code durch Unrolling oder Inlining langsamer statt schneller.
Es gibt auch andere Optimierungskriterien als wie schnell die
Instruktionen hintereinander ausgeführt werden, z.B. Kosten für
Instruction Fetch und Cache-Größe für die Instruktionen.
dunky schrieb:> Ich wage einfach mal zu behaupten, das moderne Compiler jeden guten/sehr> guten Programmierer um Längen schlagen was solche Microoptimierungen> angeht.
Insbesondere wenn man sich mal von den allersimpelsten Architekturen wie
z.B. AVR entfernt. Da reicht es dann nicht mehr, stupide die Taktzyklen
von Instruktionen zusammenzuzählen. Da können Dinge wie Caching-Effekte,
Pipeline Stalls und superskalare Architekturen einen sehr großen
Einfluss auf die Laufzeit haben.
dunky schrieb:> Ich wage einfach mal zu behaupten, das moderne Compiler jeden> guten/sehr> guten Programmierer um Längen schlagen was solche Microoptimierungen> angeht.
Ich wage zu behaupten, dass du 0 Ahnung und eine grosse Klappe hast.
Schau dir mal die Intel Performance Libs an, oder die Assembler und
Vektorbibliothek von Agner Fog.
Da ist schon bei einer memcpy ein Faktor 2-20 zwischen einer vom
Kompiler optimierten C Funktion und einer optimierten Asm Funktion
drinnen.
udok schrieb:> Ich wage zu behaupten, dass du 0 Ahnung und eine grosse Klappe hast.>> Schau dir mal die Intel Performance Libs an, oder die Assembler und> Vektorbibliothek von Agner Fog.> Da ist schon bei einer memcpy ein Faktor 2-20 zwischen einer vom> Kompiler optimierten C Funktion und einer optimierten Asm Funktion> drinnen.
Jo, die ist aber von Leuten bei Intel geschrieben, die sich über Jahre
mit so einem Kleinkram befassen. Die Aussage ist nicht "der C-Compiler
kann das besser als ein Mensch", die Aussage ist "der C-Compiler kann
das besser als DU (oder ich)".
Außerdem: ein memcpy auf x86, das 20 mal schneller ist als das aus der
glibc? Das will ich sehen.
Sven B. schrieb:> Wie immer gilt: Überlass' das mikro-optimieren dem Compiler.>> Oft wird Code durch Unrolling oder Inlining langsamer statt schneller.
Nicht unbedingt. Das inlining kann mittels Constant-Folding dafür
sorgen, dass ein ganzer Baum an (verschachtelten) inline-Aufrufen zu
einem insgesamt relativ simplen Konstrukt zusammenschnurrt, was dann im
Endeffekt kleiner und schneller als ein normaler Funktionsaufruf wird.
Das kann man insbesondere bei Register-Bit-Fummeleien sinnvoll
ausnutzen. Leider ist die Heuristik der Compiler nicht immer gut genug
um zu erkennen dass das Inlining in diesen Fällen sinnvoll ist, weshalb
man sich mit Compiler-spezifischen Attributen zum Erzwingen des Inlining
helfen kann, z.B. "__attribute__((always_inline))" beim GCC.
Sven B. schrieb:> Außerdem: ein memcpy auf x86, das 20 mal schneller ist als das aus der> glibc? Das will ich sehen.
Das will ich auch sehen :-)
https://sourceware.org/legacy-ml/libc-help/2008-08/msg00007.html
Ein Faktor 10 ist anscheinend drinnen gegenüber
dem gcc buildin bei unaligned Daten, x5 gegenüber der glibc.
Das sind aber nicht mehr ganz aktuelle Messungen, und
da müsste mal wer nachschauen, ob das noch immer so ist.
Ich schätze mal, das der gcc inzwischen besser geworden ist,
andererseits sind die Optimierungs Möglichkeiten bei modernen
i7 mit AVX512 auch gestiegen.
Ich habe mich jetzt noch gespielt, und bin auf einen Faktor 30
zwischen der schnellsten und der langsamsten Funktion gekommen!
Dazu habe ich drei einfache memcpy() Funktionen geschrieben:
1. for Schleife
2. memcpy aus der libc (immer die MSVCRT)
3. movsb Assemblerbefehl
Das ganze mit drei Compilern übersetzt, und das Ergebnis
liegt zwischen 24 Bytes/CPU-Takt und 0.8 Bytes/Cpu-Takt.
Timing wurde mit dem Pentium Timestamp Counter gemessen,
die Funktion wurde 10000 mal aufgerufen, und das schnellste
Ergebnis zählt.
Laptop Prozessor ist:
Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz, 2592 MHz, 6 Kern(e)
Die Compiler sind gut abgehangene cl.exe (MS) und icl.exe (Intel),
sowie ein moderner gcc, alle 64 Bit.
Genaue Ergebnisse sind im PDF.
1
/* dumb C solution */
2
void*testcpy(void*dst,void*src,size_tlen)
3
{
4
size_tn;
5
BYTE*p=dst;
6
BYTE*r=src;
7
8
for(n=0;n<len;n++)
9
p[n]=r[n];
10
11
returndst;
12
}
13
14
#elif SOLUTION == 1
15
16
/* libc memcpy solution */
17
void*testcpy(void*dst,void*src,size_tlen)
18
{
19
returnmemcpy(dst,src,len);
20
}
21
22
#elif SOLUTION == 2
23
24
/* simple libc independent memcpy based on "rep; movsb". */
Kann die Ergebnisse nicht so ganz nachvollziehen. Habe es auf einem
Linuxsystem probiert - habe die for-schleife weggelassen, um mehr
Iterationen durchlaufen zu können.
1
gprof:
2
3%cumulativeselfselftotal
3
4timesecondssecondscallsus/callus/callname
4
5100.461.171.1710000001.171.17testcpy_asm
5
60.871.180.0110000000.010.01testcpy_lib
Wie erwartet ist memcpy() um ein Vielfaches schneller - ein Blick in den
Quellcode zeigt, dass zunächst aligned wird um dann durch virtual
address manipulation ganze Pages zu kopieren. Der Rest wird zunächst in
Wortgröße des Prozessors und dann die letzten Bytes kopiert.
Ich kann mir nicht vorstellen, dass libc unter Windows nicht auf solche
Ansätze zurückgreift.
Danke dass du das ausprobiert hast. Ich finde udok's Ergebnisse auch
sehr unplausibel, war aber zu faul es selbst nachzubilden ;)
Den glibc Source Code habe ich mir allerdings auch angeschaut, und ja,
das sieht durchaus so aus als ob da sorgfaeltig optimiert wurde.
Wenn es so eine einfache, offensichtlich bessere Loesung gibt, warum
wuerde die glibc die dann nicht nehmen?
udok schrieb:> Ich wage zu behaupten, dass du 0 Ahnung und eine grosse Klappe hast.>> Schau dir mal die Intel Performance Libs an, oder die Assembler und> Vektorbibliothek von Agner Fog.> Da ist schon bei einer memcpy ein Faktor 2-20 zwischen einer vom> Kompiler optimierten C Funktion und einer optimierten Asm Funktion> drinnen.
ja hast Recht. Die Lib wurde bestimmt von Leuten geschrieben, welche
hier im Forum Fragen stellen. Und AssemblerVektorbibs schüttelt
natürlich auch jeder aus dem Ärmel.
Manche Leute wollen einen auch einfach falsch verstehen nur um was zu
stänkern zu haben
Zumal ich von guten & sehr guten Programmierern sprach.
Diejenigen die sowas von Hand optimieren sind exzellent und waren von
mir explizit ausgenommen :)
dunky schrieb:> Diejenigen die sowas von Hand optimieren sind exzellent
Das halte ich für ein Gerücht ;)
Die meisten, die sowas von Hand optimieren, denken sie sind exzellent
und verschlechtern durch dieses Vorgehen die Qualität ihres Codes von
"akzeptabel" nach "wtf".
Ich bezog mich auf die erwähnten Libs. Diejenigen die sowas machen sind
sicherlich keine 08/15 Durchschnittsprogrammer
Der Feld,Wald&Wiesenprogrammierer gewinnt mit solchen Microoptimierungen
aber keinen Blumentopf
Ich möchte der Diskussion noch hinzufügen dass es bei so etwas weniger
darum geht ob ein Programmierer "gut", "sehr gut" oder "schlecht" ist,
sondern darum, wofür er bezahlt wird und womit er seine Zeit verbringt.
Wenn man den Job bekommt einen Monat lang auf Teufel komm raus ein
memcopy zu optimieren, dann erwarte ich von jedem ordentlichen
Programmierer dass eine konkurrenzfähige Lösung nahe am Optimum von libc
& Co. hinbekommt. Wenn der Programmierer allerdings dafür bezahlt wird
ein Feature in einer Anwendungssoftware zu implementieren, dann würde
ich mich sehr wundern wenn er seine Zeit mit so etwas verbringen würde.