Forum: PC-Programmierung Wozu 'inline' in C++?


von Unwissender (Gast)


Lesenswert?

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?

von Εrnst B. (ernst)


Lesenswert?

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.

von DoS (Gast)


Lesenswert?

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.

von Dussel (Gast)


Lesenswert?

Soweit ich weiß, ist inline sowieso nur eine Empfehlung an den Compiler.

von Unwissender (Gast)


Lesenswert?

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).

von Luther B. (luther-blissett)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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.

von Mikro 7. (mikro77)


Lesenswert?

Vielleicht für den einen oder anderen interessant:

When to use inline function and when not to use it?
https://stackoverflow.com/questions/1932311/when-to-use-inline-function-and-when-not-to-use-it

static vs inline for functions implemented in header files
https://stackoverflow.com/questions/22102919/static-vs-inline-for-functions-implemented-in-header-files

von M.K. B. (mkbit)


Lesenswert?

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.

von M.K. B. (mkbit)


Lesenswert?

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.

von Sven B. (scummos)


Lesenswert?

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.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

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.

von inliner (Gast)


Lesenswert?

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.

von Urlaubär (Gast)


Lesenswert?


von Nop (Gast)


Lesenswert?

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.

von Sven B. (scummos)


Lesenswert?

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.

von dunky (Gast)


Lesenswert?

Ich wage einfach mal zu behaupten, das moderne Compiler jeden guten/sehr 
guten Programmierer um Längen schlagen was solche Microoptimierungen 
angeht.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

Was natürlich ein Vollblut-Assemblerprogrammierer alles im Schlaf 
überblicken und berücksichtigen kann ;)

Oliver

von udok (Gast)


Lesenswert?

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.

von Sven B. (scummos)


Lesenswert?

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.

von Programmierer (Gast)


Lesenswert?

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.

von udok (Gast)


Lesenswert?

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.

von udok (Gast)


Angehängte Dateien:

Lesenswert?

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_t len)
3
{
4
    size_t n;
5
    BYTE *p = dst;
6
    BYTE *r = src;
7
8
    for (n = 0; n < len; n++)
9
        p[n] = r[n];
10
11
    return dst;
12
}
13
14
#elif SOLUTION == 1
15
16
/* libc memcpy solution */
17
void* testcpy(void *dst, void *src, size_t len)
18
{
19
    return memcpy(dst, src, len);
20
}
21
22
#elif SOLUTION == 2
23
24
/* simple libc independent memcpy based on "rep; movsb". */
25
void testcpy(char *dst, const char *src, size_t count)
26
{
27
#if defined(_WIN32)
28
    __movsb((BYTE*)dst, (const BYTE*)src, count);
29
#elif (defined(__GNUC__) || defined(__clang__))
30
    __asm__ __volatile__("rep; movsb"
31
        : [dst] "=D"(dst), [src] "=S"(src), [count] "=c"(count)
32
        : "[dst]"(dst), "[src]"(src), "[count]"(count));
33
#else
34
    #pragma message("using generic movsb implementation")
35
    /* horrendous code bloat with Intel compiler! */
36
    while (count--) *dst++ = *src++;
37
#endif
38
}
39
40
#else
41
42
    #pragma message("solution not implemented")
43
44
#endif

Beitrag #6493890 wurde von einem Moderator gelöscht.
von glibc schneller (Gast)


Lesenswert?

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   %   cumulative   self              self     total           
3
4  time   seconds   seconds    calls  us/call  us/call  name    
4
5 100.46      1.17     1.17  1000000     1.17     1.17  testcpy_asm
5
6   0.87      1.18     0.01  1000000     0.01     0.01  testcpy_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.

von Sven B. (scummos)


Lesenswert?

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?

: Bearbeitet durch User
von dunky (Gast)


Lesenswert?

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

von dunky (Gast)


Lesenswert?

Zumal ich von guten & sehr guten Programmierern sprach.
Diejenigen die sowas von Hand optimieren sind exzellent und waren von 
mir explizit ausgenommen :)

von Sven B. (scummos)


Lesenswert?

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".

von dunky (Gast)


Lesenswert?

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

von Dirk K. (merciless)


Lesenswert?

Das ist doch nur sinnloses Getrolle vom TO
und halbherziges C++-Bashing.

merciless

von Andreas (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.