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.
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
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.
:
Bearbeitet durch User
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
:
Bearbeitet durch User
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.
Ich wage einfach mal zu behaupten, das moderne Compiler jeden guten/sehr guten Programmierer um Längen schlagen was solche Microoptimierungen angeht.
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.
Was natürlich ein Vollblut-Assemblerprogrammierer alles im Schlaf überblicken und berücksichtigen kann ;) Oliver
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_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.
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.
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
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
Das ist doch nur sinnloses Getrolle vom TO und halbherziges C++-Bashing. merciless
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.