wann'\n' und wann endl; verwenden



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


  • Mod

    tntnet schrieb:

    Also bewirkt das pubsetbuf so gar nichts.

    Wenn ich mich recht entsinne, musst du das machen, bevor irgendwelche Schreibaktionen auf dem Stream stattfinden. Eine Datei zu öffnen zählt dazu.



  • Incocnito schrieb:

    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?

    Schau mal da: http://www.cplusplus.com/reference/iostream/streambuf/pubsetbuf/: "although specific implementations may vary". Daraus lese ich, dass die spezifische Implementierung davon abweichen kann. Und die streambufs, die ich geschrieben habe, würden auch nicht darauf reagieren. Aber ansonsten hast Du dennoch recht. Das:

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

    ergibt dann das:

    $ ./flushtest -n 1000000
    flush per line:    1.162 s
    without flush:     0.031 s
    unbuffered:        0.617 s
    unbuffered stream: 1.174 s
    

    Also die performance des flush pro Zeile. Interessanterweise wird aber nicht bei jedem Zeichen geflushed sondern bei jeder Ausgabe. Bei ungepuffert hätte ich erwartet, dass jedes einzelne Zeichen einen overflow auslöst. Dem ist aber nicht so. Dennoch gibt es einen Unterschied. Ich schaue mir mit strace die Systemaufrufe an. Bei Fall 1, also der endl-Fall, wird so geschrieben: write(3, "Hello\n", 6) = 6 , bei Fall 4, also diesen forcierten ungepufferten Schreiben sehe ich das: writev(3, [{NULL, 0}, {"Hello\n", 6}], 2) = 6 .


Anmelden zum Antworten