C++ Performance
-
Was sind eigentlich die Tricks, dass C++ so schnell ist?
-
der geschreibene code wird in ziemlich gut in maschinencode gewandelt (C/C++ ist sehr systemnah)
und der maschinencode wird optimiert bis zum geht nicht mehr
variablen werden wegoptimiert, beid er compilation wird schon viel ausgerechnetaus folgendem beispiel:
int n = 5; int erg = 0, i = 0; for(i = 0; i < n; ++i) erg += i;macht der compiler ungefähr sowas hier:
int n = 5; int erg = (n*(n+1))/2;und siehe da, i ist komplett weg
-
Javaspion schrieb:
Was sind eigentlich die Tricks, dass C++ so schnell ist?
das zero-cost-principle.
man zahlt nur performance kosten wenn man es braucht - wenn ich etwas nicht brauche kostet es mich keine performance.
beispiel: speicher allokationen. ich kann alles auf den stack legen was praktisch gesehen nur eine pointer addition an zeit kostet.
oder in java ist jedes objekt ja lock-able und implementiert noch dazu das observer pattern um mit den anderen objekten kommunizieren zu koennen die gelockt sind. ob man das nun braucht oder nicht. in c++ gibt es soetwas nicht.
selbes prinzip wird bei nahezu allen sprachfeatures verwendet - so dass man nur einen bruchteil der performance kosten von anderen sprachen zahlen muss.
und natuerlich gibt es metaprogrammierung um statisch code zu generieren (und nichts ist schneller als code der nicht ausgefuehrt werden muss da er vorher schon berechnet wurde) und type_traits ermoeglichen es statisch die besten algorithmen fuer die jeweiligen datentypen zu verwenden.
das bedeutet aber auch einen gewissen mehraufwand den man als programmierer taetigen muss um diese features zu nutzen. das zero-cost-principle dagegen bekommt man ohne aufwand.
-
wobei aktuelle java compiler auch schon gewaltig optimieren. und die JIT compiler diverser VMs optimieren dann noch mehr weg, so dass im endeffekt u.u. sogar derselbe maschinencode ausgeführt wird, wie bei einem c/c++ programm.
also kurz gefasst: die hohe performance einer sprache kommt durch die optimierungsfähigkeiten des compilers zustande. und traditionsgemäß sind da die "reinen" c++ compiler am besten.
-
Skym0sh0 schrieb:
der geschreibene code wird in ziemlich gut in maschinencode gewandelt (C/C++ ist sehr systemnah)
und der maschinencode wird optimiert bis zum geht nicht mehr
variablen werden wegoptimiert, beid er compilation wird schon viel ausgerechnetaus folgendem beispiel:
int n = 5; int erg = 0, i = 0; for(i = 0; i < n; ++i) erg += i;macht der compiler ungefähr sowas hier:
int n = 5; int erg = (n*(n+1))/2;und siehe da, i ist komplett weg
das glaub ich nicht... mal abgesehen davon, dass beide Programme unterschiedliche Ergebnisse liefern (Deine Schleife müßte bis n laufen, nicht bis n-1) glaube ich nicht, dass es Compiler gibt, die das erkennen.
-
int main(int argc, char** argv) { int n = 5; int erg = 0, i = 0; for(i = 0; i < n; ++i) erg += i; std::cout<<erg; }; COMDAT _main _TEXT SEGMENT _argc$ = 8 ; size = 4 _argv$ = 12 ; size = 4 _main PROC ; COMDAT ; 28 : int n = 5; ; 29 : int erg = 0, i = 0; ; 30 : for(i = 0; i < n; ++i) ; 31 : erg += i; ; 32 : ; 33 : std::cout<<erg; 00000 8b 0d 00 00 00 00 mov ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A 00006 6a 0a push 10 ; 0000000aH 00008 ff 15 00 00 00 00 call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z ; 34 : } 0000e 33 c0 xor eax, eax 00010 c3 ret 0 _main ENDP _TEXT ENDS ENDOhne std::cout hat er es sogar ganz weg gelassen
-
Aber mit variablem n geht es nicht mehr.
int main(int argc, char** argv) { int n = argc; int erg = 0, i = 0; for(i = 0; i < n; ++i) erg += i; std::cout<<erg; }; COMDAT _main _TEXT SEGMENT _argc$ = 8 ; size = 4 _argv$ = 12 ; size = 4 _main PROC ; COMDAT ; 27 : int main(int argc, char** argv) { 00000 53 push ebx 00001 57 push edi ; 28 : int n = argc; ; 29 : int erg = 0, i = 0; ; 30 : for(i = 0; i < n; ++i) 00002 8b 7c 24 0c mov edi, DWORD PTR _argc$[esp+4] 00006 33 d2 xor edx, edx 00008 33 c9 xor ecx, ecx 0000a 33 db xor ebx, ebx 0000c 33 c0 xor eax, eax 0000e 83 ff 02 cmp edi, 2 00011 7c 12 jl SHORT $LC9@main 00013 56 push esi 00014 8d 77 ff lea esi, DWORD PTR [edi-1] $LL10@main: ; 31 : erg += i; 00017 03 d0 add edx, eax 00019 8d 4c 01 01 lea ecx, DWORD PTR [ecx+eax+1] 0001d 83 c0 02 add eax, 2 00020 3b c6 cmp eax, esi 00022 7c f3 jl SHORT $LL10@main 00024 5e pop esi $LC9@main: ; 28 : int n = argc; ; 29 : int erg = 0, i = 0; ; 30 : for(i = 0; i < n; ++i) 00025 3b c7 cmp eax, edi 00027 7d 02 jge SHORT $LN8@main ; 31 : erg += i; 00029 8b d8 mov ebx, eax $LN8@main: 0002b 03 ca add ecx, edx ; 32 : ; 33 : std::cout<<erg; 0002d 03 cb add ecx, ebx 0002f 51 push ecx 00030 8b 0d 00 00 00 00 mov ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A 00036 ff 15 00 00 00 00 call DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z 0003c 5f pop edi ; 34 : } 0003d 33 c0 xor eax, eax 0003f 5b pop ebx 00040 c3 ret 0 _main ENDP _TEXT ENDS END
-
Hm, cool.

Aber ich glaube nicht, dass er sieht, dass er die Schleife durch ne einfache Berechnung ersetzen kann. Vielmehr sieht der wohl in der Datenflußanalyse, dass er den Wert direkt berechnen kann und tut das dann -- indem er die Schleife ausführt. Mehr braucht man von einem Compiler ja auch nicht zu erwarten. Aus der Schleife den Ausdruck n*(n+1)/2 zu machen ist imo die Aufgabe des Programmierers.