Beweis: C ist schneller als C++ am Beispiel von std::vector



  • marc--us schrieb:

    wo soll hier der gag liegen? dass c einiges schneller sein kann als c++ ist doch wohl allen klar die oop verwenden.

    Nö. Dir ist offensichtlich nicht klar, wie Compiler arbeiten.

    Ob du

    bar(&foo)
    

    oder

    foo.bar()
    

    schreibst, ist letztendlich egal. In der Theorie gibt es keinen wirklichen Grund, warum die eine oder andere Sprache langsamer oder schneller sein soll. Die Unterschiede zwischen C und C++ liegen nicht in der Geschwindigkeit, sondern in den angebotenen Sprachmitteln. Und messen kannst du sowieso nur Implementationen, die aber keine allgemeingültige Aussagekraft besitzen. Und wenn man schon Laufzeit messen will, dann sollte man auch Codeäquivalente benutzen. Hier ist ja noch nicht mal garantiert, dass der Allokator von std::vector und malloc/free identisch arbeiten.



  • Simon2 schrieb:

    marc--us schrieb:

    ...dass c einiges schneller sein kann als c++ ist doch wohl allen klar...

    Also mir nicht. Da oben werden (wie so oft) "Ä mit B" verglichen. vector<> macht einfach was Anderes als ein malloc/free-Paar. Wenn man nur das Letztere braucht, kann man in C++ new[]/delete[] verwenden unst ist plötzlich genauso schnell.

    Nein. Die vector-Variante macht im Prinzip das gleiche, wie die malloc/free-Variante. Darum habe ich auf meinem System auch praktisch identische Laufzeiten. Da braucht man auch nicht zu diskutieren, warum C++ langsamer ist, wenn es so nicht ist.

    Schaut doch einfach nochmal meinen Kommentar an. Dann denkt noch mal darüber nach. Ihr diskutiert, ob eine bestimmte Implementierung das eine oder andere besser optimieren kann. Gcc hat sicher nicht den weltbeste Optimierer aber aus den beiden Varianten macht er praktisch das selbe. Und wenn es nicht das selbe sein soll, dann frage ich mich, wie gcc darauf kommt, den selben Code auszuspucken.

    Tntnet



  • tntnet schrieb:

    ...Die vector-Variante macht im Prinzip das gleiche, wie die malloc/free-Variante....

    Von "der vector-Variante" habe ich gar nicht gesprochen, sondern von der Klasse std::vector, die einiges mehr/anders macht, als free()/malloc().
    Wenn jemand diese beiden gegeneinander vergleicht, mit dem Ziel, die "Performance einer Programmierspracce" zu untersuchen, bleibe ich dabei: "Ä&B".
    Dass eine bestimmte std::vector-Implementierung in einer bestimmten Methode auch free()/malloc() verwendet, mag sein, trifft aber auf Word auch zu und macht das Argument nicht stichhaltiger.

    Gruß,

    Simon2.



  • @Plotzenhotz: Ich konnte deine Antwort bis jetzt noch nicht ganz nachvollziehen. Ich bin mit dem Debugger beide Varianten durchgegangen und beide kommen in _Fill_n (Datei: xutility, Zeile: 2757) an wo eine for-Schleife genutzt wird.

    for (; 0 < _Count; --_Count, ++_First)
    	*_First = _Val;
    

    Von einem memset konnte ich nichts entdecken.
    Wo haste denn das gesehen? Oder optimiert der Compiler das im Release-Mode zu einem memset?



  • Ah es stimmt wirklich, ich habe jetzt im Release-Mode debugged, Disassembly und Symbolnamen eingeschaltet und dann hab ich das memset gesehen.
    Ich werde mal schauen ob ich noch den Grund herausfinden kann warum er die andere Variante nicht optimiert.

    Vielleicht hatte "Antwort" doch gar nicht so unrecht 😉



  • Übrigens: Beim MinGW Compiler (g++) ist es genau umgekehrt. Dort ist das mit dem Konstruktor schnell und mit resize lahm. 😃



  • Gleich unter der der "_Fill_n" Template Funktion gibts nen non-template-overload für "char*" der memset verwendet.
    Der Unterschied zwischen den beiden Funktionen ist dass der zu setzende Wert einmal als "const _Ty&" und einmal als "int" übergeben wird.

    Wenn du den Code genau durchguckst wirst du draufkommen dass es eigentlich keinen Grund für den Compiler gibt jemals den non-template-overload zu verwenden. Tut er aber aus irgendeinem Grund. Vielleicht ein Compiler Bug. Keine Ahnung...

    Und ja, er macht das auch nur in einem Release Build.

    Und nein, der VC 8 hat keinen "lahmen" Optimizer, eher einen der besten die es derzeit gibt (abgesehen von den Bugs 🙂 ).

    Der einzige Unterschied der mir ins Auge springt wäre dass in "_Insert_n" "_Val" kopiert wird, und diese Kopie ("_Tmp") dann an "_Ufill" weitergegeben wird anstatt "_Val" selbst. Da ich aber keinen overload von "_Ufill" gefunden hätte der nicht wieder eine const-ref für sein "_Val" Parameter erzwingt weiss ich trotzdem nicht wieso jemals der non-template-overload von "_Fill_n" verwendet werden sollte.



  • Und hier der Gegenbeweis, C ist langsamer als C++.

    Programmiersprache: C

    #include <string.h>
    #include <stdlib.h>
    
    int main()
    {
    	int i;
    
    	for(i = 0; i < 10000000; ++i)
    	{
    		char* pBuffer = malloc(4000);		
    		int j;
    		for(j=0;j<10000000;j++)memset(pBuffer, 0, 4000);
    		free(pBuffer);
    	}
    }
    

    Programmiersprache: C++

    #include <vector>
    
    int main()
    {
    	for(int i = 0; i < 10000000; ++i)
    	{
    		char* pBuffer = new char[4000];
    		memset(pBuffer, 0, 4000);
    		delete[] pBuffer;
    	}
    }
    

    Schon seltsam, gelle?



  • Danke Plotzenhotz für die Antwort! Ich glaube das das _Fill_n mit dem memset mit der Sache gar nichts zu tun hat. Ich habe alle Funktionen der Datei xutility auskommentiert in denen memset vorkommt und er generiert trotzdem noch ein memset im Release-Build. Ich denke mal der sieht das der Schleife einfach an. 😉

    Und die Lösung ist wirklich eine Kopie von _Val zu ziehen. Dann ists auch mit dem Konstruktor so schnell wie in C. 😉

    Also:

    void _Construct_n(size_type _Count, const _Ty& _Val)
    {
        // ...
        _Ty tmp = _Val;
        _Mylast = _Ufill(_Myfirst, _Count, tmp);
        // ...
    }
    

    So und jetzt installiere ich lieber Visual C++ neu weil ich soviel an den Header-Dateien rumgefrickelt hab. 😉



  • David_pb schrieb:

    Schau dir den einfach den Quellcode an...

    was fuern dummer spruch!



  • phlox81 schrieb:

    C kann keine OOP

    Wat?!

    Natürlich kann C OOP. Nur halt keine Vererbung und keine Templates. Offiziell. Aber dafür kann ich mir in C die gleichen Strukturen zusammenbasteln, das muss ich dann zwar von Hand machen, aber ohne Probleme umsetzbar. Und wie SeppJ (?) neulich auch bereits gesagt hat: die ersten C++-Compiler haben über den Präprozessor C-Code generiert und dann kompiliert.

    phlox81 schrieb:

    keine Templates, bei komplexeren Problemstellungen wirst du also schnell einen
    erheblichen Mehraufwand haben.

    Jaaa - der Linux-Kernel hatte diese Probleme auch. Wenn die nicht damals auf C++ umgestiegen wären - nicht auszudenken, wo die heute mit ihrer Komplexität wären. 🤡

    Spaß beiseite. "Große" C++-Software, in die ich bisher geschaut habe (Firefox, Libreoffice, Thunderbird) benötigen Stunden, bis sie kompiliert werden (und das auf einem Haswell-Octacore) und sind meines Erachtens nicht einfach wartbar. Kann auch daran liegen, dass die Codequalität mies ist, das will ich gar nicht in Abrede stellen, aber ... wie sagt man? In C++ ist es schwerer, sich in den Fuß zu schießen, aber wenn man's schafft, fliegt das Bein mit? Aus meiner persönlichen Erfahrung kann ich nur noch sagen: stimmt.

    marc--us schrieb:

    wo soll hier der gag liegen? dass c einiges schneller sein kann als c++ ist doch wohl allen klar die oop verwenden. dafür ist das konzept stimmiger. hardware wird doch alle 18 monate doppelt so schnell. da kann man dann schon von assembler weg gehen und vielfach verschachtelte hochsprachen verwenden. 😉

    Abgesehen davon, dass C auch OOP kann ... was wird denn schneller?

    Meines Wissens ist ein ziemlich großes Problem in der "schnellen Programmierung" derzeit der Hauptspeicher bzw. der Anschluss daran. Wenn ein Cache-Miss passiert, darf die CPU gerne bis zu 200 Cycles Däumchen drehen, bis die Cache Line vom Hauptspeicher geladen ist. Ein Register-Miss und das Laden aus dem First Level kostet nur im einstelligen Cycles-Bereich.

    Die Prozessoren werden vielleicht schneller. Aber beim Hauptspeicher haben wir mit physikalischen Limitierungen zu kämpfen (sogar noch mehr, wenn es Von-Neumann-Maschinen sind, bei denen Daten und Code über einen Bus gesendet werden müssen). Es überrascht mich kein bisschen, dass AVX in Speicherbenchmarks langsamer ist als SSE, weil die Synchronisierung bei 256-Bit einfach schwieriger und langsamer ist als bei 128 Bit.

    Aber das wirklich Witzige ist: ordentlichen Code, der so etwas berücksichtigt, kann man auch in C++ schreiben. Ist schwieriger, weil mehr unter der Haube passiert. Aber machbar.


  • Mod

    Tut das Not, einen viele Jahre alten Thread aus zu graben? Ich weiß, er wurde kürzlich verlinkt, aber das war rein des Lesens wegen, nicht um die Diskussion wieder auf zu heizen. Die Leute, die in diesem Thread etwas geschrieben haben, sind überwiegend seit Jahren nicht mehr aktiv; der überwiegende Teil der derzeit aktiven Nutzer ist weitaus jünger als dieser Thread; und selbst für den minimalen Überlapp beider Gruppen ist es ziemlich sinnlos, mit jemandem darüber zu diskutieren, was er vor 10 Jahren mal irgendwo geschrieben hat, egal für wie korrekturbedürftig du diese Aussagen hältst. Das ist einfach nur unhöflich gegenüber allen anderen Forennutzern, egal ob alt oder neu.


Anmelden zum Antworten