wann'\n' und wann endl; verwenden



  • Hallo, ich habe zwar was dazu gelesen, aber leider nicht wirklich verstanden was dieses Flushen beim endl bewirkt. Kann mier jemand erklären was genau der Unterschied ist.

    Danke.



  • Es ist oftmals ineffizient, einzelne neue Zeichen/Elemente jedes Mal sofort auf dem Bildschirm auszugeben oder in eine Datei zu schreiben (Bevor geschrieben oder gemalt wird und auch danach, geht so viel Zeit mit Organisation ins Land, dass man besser mehrere Zeichen/Elemente auf einmal malt/schreibt).
    Aus diesem Grund haben die meisten Ziele von Streams eingebaute "Puffer", die erstmal etwas Daten anhäufen und dann Paketweise zeichnen/speichern. Mit "flush" signalisierst du, dass dieser Puffer umgehend an das Ziel befördert werden soll. Das kann natürlich viel früher sein, als das System ansonsten es tun würde und schon kommen wir wieder zu diesen ineffizienzen, die ich eingangs beschrieb.



  • Einfach gesagt:
    `'\n' = Neue Zeile

    endl = '\n' + flush`

    Die Faustregel ist einfach: Brauchst du kein flush, nimmst du kein endl.



  • Wenn du z.B. schreibst:

    cout << "Hallo Welt";
    

    dann wird "Hallo Welt nicht sofort auf die Konsole geschrieben, sondern zuerst in einen Puffer. Damit jetzt der Inhalt vom Puffer auf die Konsole kommt, musst ein Flush gemachen werden. Es ist unnötig, dass du das übernimmst, da es meist automatisch geschieht. Darum reicht meist immer ein einfaches '\n' aus.



  • Ok, danke für die Antworten. Wann tritt den der Fall auf, indem man ein Flush braucht bzw. wenn es nicht automatisch geschieht?



  • Namal schrieb:

    Ok, danke für die Antworten. Wann tritt den der Fall auf, indem man ein Flush braucht bzw. wenn es nicht automatisch geschieht?

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



  • out schrieb:

    Namal schrieb:

    Ok, danke für die Antworten. Wann tritt den der Fall auf, indem man ein Flush braucht bzw. wenn es nicht automatisch geschieht?

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

    int main()
    {
      std::cout << "wichtig" << '\n';
      zehn_stunden_rechnen();
      std::cout << std::flush;
    }
    

    edit: also man sollte halt überlegen, ob man cout nicht vor der fkt flushen sollte... nicht, dass du jetzt denkst, dass die ausgabe sonst niemals auftaucht... ^^



  • 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.



  • Wie schon gesagt wurde, sind die C++ Streams buffered, d.h. wenn du etwas ausgibst, dann wird das in der Regel nicht direkt auf das Ausgabemedium (z.B. in die Datei) geschrieben, sondern erst in einen kleinen Pufferspeicher. Immer, wenn dieser Speicher voll ist, wird dann der ganze Block auf einmal rausgeschrieben. Hintergrund: Performance; es ist in der Regel wesentlich schneller, wenige große Blöcke rauszuschreiben, als haufenweise kleine.
    flush() erzwingt nun das "Runterspülen" des Pufferspeichers, also das Rausschreiben der Daten. Verwende es immer dann, wenn sichergestellt werden muss, dass alles, was bis zu einem bestimmten Zeitpunkt ausgegeben wurde, auch tatsächlich im Ausgabemedium (Datei, Bildschirm) steht.

    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...

    Ein anderes Beispiel, wo es auf jeden Fall einen Unterschied macht, wäre z.B. eine Logdatei. Dort willst du wohl nach jeder Ausgabe ins Log flush() aufrufen, damit sichergestellt ist, dass deine Ausgabe auch tatsächlich in der Datei landet. Denn wenn dein Programm irgendwann abstürzt, ist der Inhalt des Log ansonsten praktisch wertlos, da man von der Logdatei nicht genau auf den Zeitpunkt des Absturzes schließen kann, da du nicht weißt, was vielleicht noch alles im Puffer stand...

    Edit: Nur falls die Frage auftaucht: Wenn eine Datei geschlossen wird, wird natürlich automatisch alles rausgeschrieben...


  • Mod

    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.

    Meiner Erfahrung nach ist das wesentlich mehr. Vielleicht wenn du alleine an einem Rechner mit großem Plattencache und schnellem Dateisystem sitzt. Aber auf einem verteilten Mehrbenutzersystem mit schwachem Dateisystem kann ein häufiges flushen viele Größenordnungen ausmachen - und zwar nicht nur für dich. Damit kann man ganze Clusterrechner praktisch lahmlegen.


  • 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


Log in to reply