Fehlerhaftes Verständnis von Templates ?



  • Cpp Tech schrieb:

    // ... 
    
    	cout << endl << endl;
    
    // ...
    

    Yeah. Ich würd' zur Sicherheit danach noch flushen:

    cout << endl << endl << flush;
    

    👍



  • Swordfish schrieb:

    Cpp Tech schrieb:

    // ... 
    
    	cout << endl << endl;
    
    // ...
    

    Yeah. Ich würd' zur Sicherheit danach noch flushen:

    cout << endl << endl << flush;
    

    👍

    Ich würde mehr Klammern setzen, bei endl immer umbrechen und das Semikolon dann verdoppeln, damit man sieht, daß die Zeile fertig ist.

    (((cout << 
    		endl) << 
    			endl) << 
    				flush) ;
    


  • Herzlich Dank an alle für den Hinweis!

    Im Namespace std gibt es eine Funktion max. Ich nehme an in deinem Code steht ein using namespace std, richtig? Das führt dann natürlich zu einem Konflikt. Deshalb: using namespace std pöse 😉

    Und tatsächlich, jetzt verstehe ich... Nachdem ich den Namen der Funktion geändert habe, funktionierte alles wie gewünscht.

    Yeah. Ich würd' zur Sicherheit danach noch flushen:

    cout << endl << endl << flush;
    

    Danke für den Rat, ich kannte den Manipulator "flush" bisher noch gar nicht. Er leert den Puffer und lässt alle Ausgaben sofort erscheinen, wenn ich das richtig verstanden habe (Stichwort "sauberer" Programmcode)?



  • Cpp Tech schrieb:

    Danke für den Rat, ich kannte den Manipulator "flush" bisher noch gar nicht. Er leert den Puffer und lässt alle Ausgaben sofort erscheinen, wenn ich das richtig verstanden habe (Stichwort "sauberer" Programmcode)?

    Der Rat von Swordfish war nicht ernst gemeint, denn std::endl flushst automatisch 😉 . Lies einmal C++ Reference - std::endl, dann dürfte alles klar werden.


  • Mod

    Mehr noch: Normalerweise willst du nicht flushen (also auch nicht endl), denn in allen gängigen Szenarien, wo du so etwas wünschen könntest, passiert es bereits automatisch, und in allen anderen Szenarien ist es überflüssiger Overhead.



  • Da es mich interessiert hat wie gross der Performanceunterschied sein kann habe ich versucht einen kurzen Benchmark zu schreiben.
    Ein paar zufällige Integer werden zeilenweise in ein File geschrieben wobei zuerst '\n' und anschliessend std::endl für den Zeilenumbruch verwendet wird.

    Hier der Code:

    #include <chrono>
    #include <iostream>
    #include <fstream>
    #include <vector>
    
    int main(int argc, char *argv[])
    {
        srand(time(nullptr));
    
        if ( argc != 2 )
        {
            std::cerr << "Usage: " << argv[0] << " " << "MB" << std::endl;
            exit(1);
        }
    
        uint64_t size = atol(argv[1]) << 20;
    
        std::vector<int> rand_data;
        for ( size_t i = 0; i < size / sizeof(int); ++i )
            rand_data.push_back(rand());
    
        uint64_t count,duration;
        std::chrono::time_point<std::chrono::system_clock> startP, endP;
        {
            startP = std::chrono::system_clock::now();
    
            std::ofstream buffered_output("buffered.out");
            for ( auto data : rand_data )
                buffered_output << data << '\n';
    
            endP = std::chrono::system_clock::now();
            duration = std::chrono::duration_cast<std::chrono::nanoseconds>(endP-startP).count();
            std::cout << "Using \\n:\t" << count << '\t' << (duration/1.0E9) << " sec \t"
                      << (10000.0*size)/(duration) << " GB/s\n";
        }
        {
            startP = std::chrono::system_clock::now();
    
            std::ofstream buffered_output("unbuffered.out");
            for ( auto data : rand_data )
                buffered_output << data << std::endl;
    
            endP = std::chrono::system_clock::now();
            duration = std::chrono::duration_cast<std::chrono::nanoseconds>(endP-startP).count();
            std::cout << "Using endl:\t" << count << '\t' << (duration/1.0E9) << " sec \t"
                      << (10000.0*size)/(duration) << " GB/s\n";
        }
    }
    

    Kompiliert habe ich ganz einfach mit

    g++ -O2 -std=c++11 -o main main.cpp

    Resultat fürr 10MB an Daten:

    Using \n: 0 0.561771 sec 186.655 GB/s
    Using endl: 0 72.7497 sec 1.44135 GB/s

    Der Unterschied ist ziemlich gross.

    PS:
    Ich habe kaum Erfahrung mit Benchmarking. Bin also für Feedback dankbar 😉



  • Lustigerweise ist bei nem Stringstream genau umgekehrt. Zwar nicht so extrem wie bei Dateien, aber doch merklich.



  • Stimmt, ich hab obigen Code mit stringstream statt ofstream ausgeführt und folgendes erhalten:

    Using \n: 0 2.55661 sec 410.144 GB/s
    Using endl: 0 2.36814 sec 442.785 GB/s



  • Skym0sh0 schrieb:

    Lustigerweise ist bei nem Stringstream genau umgekehrt. Zwar nicht so extrem wie bei Dateien, aber doch merklich.

    Du willst doch nur wieder endl schreiben.

    size=1M
    Using \n:	0	0.0187179 sec 	560.201 GB/s
    Using endl:	0	0.0176288 sec 	594.808 GB/s
    
    size=10M
    Using \n:	0	0.184224 sec 	569.184 GB/s
    Using endl:	0	0.178102 sec 	588.749 GB/s
    
    size=100M
    Using \n:	0	1.90732 sec 	549.764 GB/s
    Using endl:	0	1.84805 sec 	567.395 GB/s
    
    size=1000M
    Using \n:	0	18.7715 sec 	558.601 GB/s
    Using endl:	0	18.0149 sec 	582.06 GB/s
    
    size=1M
    Using endl:	0	0.0222931 sec 	470.359 GB/s
    Using \n:	0	0.0218076 sec 	480.831 GB/s
    
    size=10M
    Using endl:	0	0.177248 sec 	591.587 GB/s
    Using \n:	0	0.185553 sec 	565.109 GB/s
    
    size=100M
    Using endl:	0	1.80715 sec 	580.238 GB/s
    Using \n:	0	1.91158 sec 	548.538 GB/s
    
    size=1000M
    Using endl:	0	17.784 sec 	589.619 GB/s
    Using \n:	0	18.8306 sec 	556.847 GB/s
    

    edit: Uuups, hatte das Konstruktorargument (den Dateinamen) stehenlassen. Ihr auch?

    size=1M
    Using endl:	0	0.0180045 sec 	582.395 GB/s
    Using \n:	0	0.0191331 sec 	548.044 GB/s
    
    size=10M
    Using endl:	0	0.178203 sec 	588.418 GB/s
    Using \n:	0	0.188795 sec 	555.406 GB/s
    
    size=100M
    Using endl:	0	1.84706 sec 	567.699 GB/s
    Using \n:	0	1.94918 sec 	537.956 GB/s
    
    size=1000M
    Using endl:	0	18.0015 sec 	582.494 GB/s
    Using \n:	0	18.7693 sec 	558.664 GB/s
    

  • Mod

    Die GB/s sollten auf jedenfall nochmal überarbeitet werden. So schnell ist ja normalerweise nicht mal der L1-Cache.



  • Ich hab' mir auch gedacht dass hier irgendwo ein Rechenfehler drin sein muss.
    War aber zu faul es nachzurechnen 😉



  • Also für mich sieht das jetzt aber nicht so aus als ob endl so böse wäre wie hier oft dargestellt, da sind ja immer nur ca. 1-5% Unterschied?



  • happystudent schrieb:

    Also für mich sieht das jetzt aber nicht so aus als ob endl so böse wäre wie hier oft dargestellt, da sind ja immer nur ca. 1-5% Unterschied?

    72.7497 zu 0.561771 sind 1-5%? 😕



  • Bei Stringstreams ist der Unterschied nicht so gravierend, weil deren Synchronisation nichts mit dem OS zu tun hat ...



  • ASAP schrieb:

    72.7497 zu 0.561771 sind 1-5%? 😕

    Nein, aber 18.7715 sec zu 18.0149 sec zum Beispiel. Oder sämtliche sonstigen Werte aus Volkarts Benchmark. Das endl ist da ja sogar schneller als das '\n' . Von daher:

    Swordfish schrieb:

    Bei Stringstreams ist der Unterschied nicht so gravierend, weil deren Synchronisation nichts mit dem OS zu tun hat ...

    Was heißt "nicht so gravierend", es ist ja sogar anders rum.



  • Ich weiß nicht, was volkard gemessen hat. Aber der schlimmste Fall ist halt, wenn's eine Datei ist, was icarus2 auch gemessen hat.



  • Warum nicht einfach std::endl nie nehmen? Wenn wir von 500+-30 GB/s reden, wird's daran nicht liegen, wenn eine Anwendung schneckt.

    Komische Streams, die selber zur Laufzeit schauen, ob '\n' geschickt wurde und dann in ihren Einstellungen nachschauen, ob sie dann fluschen müssen, reden wir von sowas? Falls ja, sind wir eigentlich bei der falschen Sprache oder dringend bei den falschen Streams. Da muss dann locker bleiben. Ohne Änderung am Code könnte man wenn man wöllte…



  • Swordfish schrieb:

    Ich weiß nicht, was volkard gemessen hat. Aber der schlimmste Fall ist halt, wenn's eine Datei ist, was icarus2 auch gemessen hat.

    Ich hab nur stringstream gemessen, alles im RAM und keine Auslagerungsdatei.
    Bei Dateien macht endl locker meinen Intel-i7 zu einem Commodore-64-er.


Anmelden zum Antworten