wann'\n' und wann endl; verwenden


  • Administrator

    dot schrieb:

    Beispiel: Wenn du "Bitte geben sie xyz ein:" auf die Konsole ausgibst und dann auf eine Eingabe wartest, solltest du vor dem Warten auf die Eingabe flushen, damit der Benutzer auch ganz sicher die Aufforderungsmeldung zu sehen bekommt und nicht nur z.B. den halben Text oder überhaupt gar keinen Text gefolgt von einem blinkenden Cursor, der auf eine Eingabe wartet. Unter aktuellen Betriebssysteme wird sowas im Normalfall allerdings vermutlich grundsätzlich nicht passieren, da Konsolenoutput dort in der Regel wohl nicht gepuffert sein wird (weil er sowieso in den RAM geht) und flush() dort dann keinen wirklichen Effekt hat. Wenn der User allerdings z.B. durch ein SSH Terminal übers Netzwerk verbunden ist, oder der Output deines Programmes in eine Datei umgeleitet wird, könnte es schon wieder einen sehr wesentlichen Unterschied machen...

    Dazu braucht es unter C++ aber z.B. bei std::cout und std::cin (bzw. wcout und wcin ) gar kein Flush, weil dieser automatisch passiert, sobald eine Eingabe erwartet wird. Dazu kann man bei eigenen Streams die Funktion std::basic_ios::tie verwenden.

    cppreference.com schrieb:

    By default, the standard streams cin, cerr and clog are tied to cout. Similarly, their wide counterparts wcin, wcerr and wclog are tied to wcout.

    ISO/IEC 14882:2011 - 27.4.2 Narrow stream objects - Absatz 2 schrieb:

    After the object cin is initialized, cin.tie() returns &cout. ...

    ISO/IEC 14882:2011 - 27.4.3 Wide stream objects - Absatz 2 schrieb:

    After the object wcin is initialized, wcin.tie() returns &wcout. ...

    Wodurch es nun noch weniger Gründe gibt, wieso man selber Flush aufrufen muss. Man kann eigentlich sagen, im Normalfall sollte man das vergessen. Flush ruft man nur in Spezialfällen auf, wo man genau weiss, dass es dieses Flush benötigt.

    std::endl sollte aus Tutorials und Bücher für Anfänger eher verbannt werden.

    Grüssli



  • out schrieb:

    Gute Frage, ich entwickel nur unter Windows, da fällt mir grad kein Beispiel ein, wo man nachhelfen muss.

    Unter Windows ist stdout auch Zeilengepuffert. '\n' impliziert somit ein flush .



  • Swordfish schrieb:

    out schrieb:

    Gute Frage, ich entwickel nur unter Windows, da fällt mir grad kein Beispiel ein, wo man nachhelfen muss.

    Unter Windows ist stdout auch Zeilengepuffert. '\n' impliziert somit ein flush .

    quelle?


  • Mod

    Swordfish schrieb:

    out schrieb:

    Gute Frage, ich entwickel nur unter Windows, da fällt mir grad kein Beispiel ein, wo man nachhelfen muss.

    Unter Windows ist stdout auch Zeilengepuffert. '\n' impliziert somit ein flush .

    Ich vermute, du verwechselst stdout mit der Konsolenausgabe (dann wäre es genau so wie unter Linux).



  • Sone schrieb:

    Tatsächlich macht ein Aufruf von std::endl meist einen sehr großen Einfluss auf das Schreiben in bspw. filestreams. Hat jemand dazu Messdaten? Ich hab' gelesen, dass es die Ausgabe bis zu Zwei mal langsamer machen kann.

    Ich habe gerade mal auf die schnelle mal ein einfaches Testprogramm geschrieben. Das ist das Ergebnis:

    $ ./flushtest -n 1000000
    flush per line: 1.211 s
    without flush:  0.035 s
    

    Das Programm ist hier:

    #include <iostream>
    #include <fstream>
    #include <cxxtools/arg.h>
    #include <cxxtools/clock.h>
    #include <cxxtools/timespan.h>
    
    int main(int argc, char* argv[])
    {
      cxxtools::Arg<unsigned> lines(argc, argv, 'n', 10);
      cxxtools::Clock clock;
    
      clock.start();
      {
        std::ofstream out("out1.txt");
        for (unsigned n = 0; n < lines; ++n)
          out << "Hello" << std::endl;
      }
      cxxtools::Timespan t1 = clock.stop();
    
      clock.start();
      {
        std::ofstream out("out2.txt");
        for (unsigned n = 0; n < lines; ++n)
          out << "Hello\n";
      }
      cxxtools::Timespan t2 = clock.stop();
    
      std::cout << "flush per line: " << t1.totalMSecs()/1e3 << " s\n"
                   "without flush:  " << t2.totalMSecs()/1e3 << " s\n";
    }
    

    Ich habe meine cxxtools-library verwendet. Die Funktionen sollten selbsterklärend sein. Übersetzt habe ich das Programm mit: g++ -o flushtest -lcxxtools -O2 flushtest.cpp .

    Das System ist ein Fedora 17 Linux.

    Das Verhältnis zwischen gepufferten und ungepufferten schreiben hängt natürlich von vielen Faktoren ab. Beispielsweise von der Länge der Zeile. Ist die Zeile so ca. 8000 Zeichen lang, wird kein Unterschied messbar sein, da der ofstream normalerweise nach 8k automatisch flushed.



  • Hallo tntnet,

    könntest du auch mal ungepuffert testen, also

    out.setbuf(0, 0);
    

    Ich selber halte nämlich nicht viel von der Pufferung - insbesondere beim Auslesen (großer Dateien) habe ich dadurch schon deutliche Performancesteigerungen erreichen können.



  • Durch die Erhöhung der Anzahl von Systemcalls, Prozesskontext-Switches, Kernelmode wechseln usw. hast du eine Erhöhung der Performance erreicht? O.O



  • Dravere schrieb:

    Dazu braucht es unter C++ aber z.B. bei std::cout und std::cin (bzw. wcout und wcin ) gar kein Flush, weil dieser automatisch passiert, sobald eine Eingabe erwartet wird.

    Ok, das war mir nicht bewusst, danke für die Klarstellung. Dann bleibt nur das Beispiel mit dem Logfile...



  • Decimad schrieb:

    Durch die Erhöhung der Anzahl von Systemcalls, Prozesskontext-Switches, Kernelmode wechseln usw. hast du eine Erhöhung der Performance erreicht? O.O

    Es gibt noch mehr Buffer den weg runter. Und wenn man nur eine komplette datei in den speicher streamt doer aus dem speicher auf die Festplatte, dann ist der zusätzliche Puffer im stream in der Tat überflüssig



  • Dann verstehe ich jetzt gar nicht mehr, warum die Streams überhaupt puffern, wenn sie doch sowieso in den OS-spezifischen Runtimes implementiert werden, die doch wissen könnten, dass da noch ein anderer Puffer ist Oo.
    Aufklärungsbedarfsalarm! Tuuttuuut



  • Decimad schrieb:

    Dann verstehe ich jetzt gar nicht mehr, warum die Streams überhaupt puffern, wenn sie doch sowieso in den OS-spezifischen Runtimes implementiert werden, die doch wissen könnten, dass da noch ein anderer Puffer ist Oo.
    Aufklärungsbedarfsalarm! Tuuttuuut

    Seit wann gibt es C++? Und seit wann sind Betriebssysteme so schlau? Dazu kommt, dass es einfach ein Feature ist; vielleicht ist es nicht nötig, oder nur selten, aber manchmal ist es nötig.



  • Gibt C++ denn vor, wie groß die Puffer standardmäßig zu sein haben? Die aktuellen C++-Runtimes stammen ja nun wahrscheinlich nicht aus grauer Vorzeit...

    Ich lese mir gerade mal wieder die iostream-Doku durch und muss einfach feststellen, dass die Autoren damals offenbar Anhänger des Films "Das Leben des Brian" waren...



  • Decimad schrieb:

    Dann verstehe ich jetzt gar nicht mehr, warum die Streams überhaupt puffern, wenn sie doch sowieso in den OS-spezifischen Runtimes implementiert werden, die doch wissen könnten, dass da noch ein anderer Puffer ist Oo.
    Aufklärungsbedarfsalarm! Tuuttuuut

    Tuuttuuut <- Das war mein Stichwort.
    Weil du aus C++ heraus steuern können möchtest, ob ein Stream gepuffert wird oder nicht. Wenn du diese Aufgabe dem System überlässt - wie kann es dann zwischen Streams die gepuffert werden sollen, und z.B. Fehlerlogs unterscheiden?



  • Na auf mich machte jetzt TH69's Beispiel den Eindruck, dass man für bestimmte Streams erstmal nur steuern kann, ob man n Puffer verwendet, oder n+1! Wobei der n+1'te Puffer eigentlich nutzlos ist, aber per default einfach mal angestellt ist, damit man ihn als Experte abschalten kann und dafür belohnt wird! 😃



  • Decimad schrieb:

    Na auf mich machte jetzt TH69's Beispiel den Eindruck, dass man für bestimmte Streams erstmal nur steuern kann, ob man n Puffer verwendet, oder n+1! Wobei der n+1'te Puffer eigentlich nutzlos ist

    Wer sagt das? Ich sicherlich nicht. Die Puffer weiter unten sind garantiert nicht auf viel kleine Schreibmengen ausgelegt(oder ligen auf recht tiefen Schichten im Betriebssystem). Das verhält sich wohl ähnlich zu new: new ist lahm, so lahm dass man sich für kleine Objekte eine andere Verwaltungsstrategie überlegen sollte.



  • Th69 schrieb:

    Hallo tntnet,

    könntest du auch mal ungepuffert testen, also

    out.setbuf(0, 0);
    

    Ich selber halte nämlich nicht viel von der Pufferung - insbesondere beim Auslesen (großer Dateien) habe ich dadurch schon deutliche Performancesteigerungen erreichen können.

    Setbuf gibt es nicht. Aber ich habe folgendes in das Programm eingefügt:

    clock.start();
      {
        int fd = ::open("out3.txt", O_WRONLY|O_CREAT, 0666);
        const char data[] = "Hello\n";
        for (unsigned n = 0; n < lines; ++n)
          ::write(fd, data, sizeof(data) - 1);
        ::close(fd);
      }
      cxxtools::Timespan t3 = clock.stop();
    

    Und die Ausgabe ergänzt. Das Ergebnis ist das:

    flush per line: 1.186 s
    without flush:  0.036 s
    unbuffered:     0.626 s
    

    Ich halte viel von der Pufferung.



  • Decimad schrieb:

    Dann verstehe ich jetzt gar nicht mehr, warum die Streams überhaupt puffern, wenn sie doch sowieso in den OS-spezifischen Runtimes implementiert werden, die doch wissen könnten, dass da noch ein anderer Puffer ist Oo.
    Aufklärungsbedarfsalarm! Tuuttuuut

    Der Puffer eines iostreams ist sehr "nahe" am Programm.

    Der Puffer des OS ist sehr weit entfernt - da sind etliche Funktionsaufrufe, Validierung von Parametern, diverse Lookups etc. sowie oft noch zumindest eine Kernelmode-Transition dazwischen.

    Wenn man also viele kleine Stücke aus einem Stream liest, oder in einen Stream schreibt, dann bringt die Pufferung im Stream einiges.



  • Wenn man das Buffern durch streambuf ausmachen will benutzt man

    std::cout.rdbuf()->pubsetbuf(0, 0);
    

    Siehe hier. Sollte man sich sowieso mal komplett durchlesen den Abschnitt "Input and Output", echt nützliche Infos da drin.



  • Das Problem ist nur, dass die spezifische Implementierung den Puffer beim Overflow einfach neu setzen kann. Oder wie auch immer. Auf jeden Fall habe ich folgendes eingefügt:

    clock.start();
      {
        std::ofstream out("out4.txt");
        out.rdbuf()->pubsetbuf(0, 0);
        for (unsigned n = 0; n < lines; ++n)
          out << "Hello\n";
      }
      cxxtools::Timespan t4 = clock.stop();
    

    Und das Ergebnis diesmal:

    $ ./flushtest -n 1000000
    flush per line:    1.192 s
    without flush:     0.031 s
    unbuffered:        0.629 s
    unbuffered stream: 0.033 s
    

    Also bewirkt das pubsetbuf so gar nichts.



  • Das Problem ist nur, dass die spezifische Implementierung den Puffer beim Overflow einfach neu setzen kann.

    Also das hör ich zum ersten mal. Ansonsten hätte ich gesagt, ich weise dem Ofstreamobjekt einfach nen neuen Buffer zu und überschreib dabei die overflow Funktion. Nur keine Ahnung wie die da aussieht..
    Kann man bei ofstream überhaupt nen neuen streambuf zuweisen?


Anmelden zum Antworten