Blockweise Kopie in IO-Streams



  • Was ist die effizienteste Möglichkeit, Blöcke aus Filestreams zu kopieren? Ich hab momentan einen Puffer, aber glaube, dass der unnötig ist. Ausserdem muss ich so bei grossen Blöcken viel Speicher allokieren, eventuell wäre daher eine schrittweise Kopie besser geeignet. Bieten die IO-Streams schon sowas an?

    Mein momentaner Code (noch ohne Fehlerbehandlung):

    void Copy(std::istream& source, std::streampos srcBegin, std::streamoff srcSize,
    	std::ostream& dest, std::streampos dstBegin)
    {
    	std::vector<char> buffer( static_cast<std::size_t>(srcSize) );
    
    	source.seekg(srcBegin);
    	source.read(buffer.data(), buffer.size());
    
    	dest.seekp(dstBegin);
    	dest.write(buffer.data(), source.gcount());
    }
    


  • Stream-Iteratoren könnten helfen.


  • Mod

    Jodocus schrieb:

    Stream-Iteratoren könnten helfen.

    Nee. Ein istream_iterator nutzt Operator >>, was gar nicht in Frage kommt und ein istreambuf_iterator nutzt streambuf::sgetc() was auch nicht das wahre ist, wenn man doch eigentlich streambuf::sgetn() haben möchte. read ist schon das richtige. Ebenso beim Ausgabestrom.

    Das ist schon ganz ok, so wie es ist. Was man noch machen könnte:
    - Möchte man gleich alles kopieren, kann man auch direkt den ganzen Streambuf des Eingabestroms in den Ausgabestrom schreiben.
    - Du könntest dir den vector sparen und stattdessen mit einer Schleife in Blöcken (vorgeschlagene Blockgröße: 0.5 bis 8 kB, mal ein bisschen experimentieren). Das spart dir einmalig die Speicheranforderung, könnte eventuell sogar schneller sein, falls die Quelle/Ziel der Streams langsam sein sollten, da dann die Eingabe und die Ausgabe gleichzeitig laufen, anstatt erst alles zu lesen und dann alles zu schreiben.



  • Also

    #include <fstream>
    #include <iterator>
    #include <algorithm>
    
    using namespace std;
    
    int main()
    {
    	ifstream i("Infile", ios_base::binary);
    	noskipws(i);
    	ofstream o("Outfile", ios_base::trunc | ios_base::binary);
    	std::copy(istream_iterator<char>(i), istream_iterator<char>(), ostream_iterator<char>(o));
    }
    

    funktioniert bei mir.

    Aber Blockweises read()/write() dürfte um Längen schneller sein.


  • Mod

    Das es funktioniert bestreite ich gar nicht, aber es war ausdrücklich nach Effizienz gefragt. istream_operator ist eine formatierte Leseaktion, da wird ungeheuer viel bei gerechnet und ist entsprechend lahm. Die Funktionen sind nicht so klug, dass sie für den Sonderfall "copy mit einem istream_iterator mit char als Templateargument und auf dem Stream ist noskipws aktiviert" eine effiziente Sonderimplementierung mit read und write anbieten.



  • Was ist die effizienteste Möglichkeit...?

    Nicht mal das Lesen klappt heute. 🙄

    Ich geh' wieder ins Bett...



  • Vielen Dank. Gibt es keine Möglichkeit, Teile direkt aus einem Stream in den anderen zu kopieren (z.B. über den Streambuf)?

    Sonst versuch ichs mit der vorgeschlagenen Schleife.



  • Was spricht denn gegen den streambuf_iterator? Der liest die chars zwar einzeln aus, aber die werden ja vom Stream gebuffert. Das Buffern zweimal zu machen, kostet da nur Performanz.



  • SeppJ schrieb:

    - Du könntest dir den vector sparen und stattdessen mit einer Schleife in Blöcken (vorgeschlagene Blockgröße: 0.5 bis 8 kB, mal ein bisschen experimentieren).

    Ich würde den Puffer um zwei Grössenordnungen grösser machen.
    Also so 64KB bis 1MB.


  • Mod

    angström schrieb:

    Was spricht denn gegen den streambuf_iterator? Der liest die chars zwar einzeln aus, aber die werden ja vom Stream gebuffert. Das Buffern zweimal zu machen, kostet da nur Performanz.

    Stimmt einfach nicht. Ich habe mal einen Benchmark geschrieben:

    const size_t block_size = 4096;
                size_t num_blocks = heap.size() / block_size;
                size_t rest = heap.size() - num_blocks * block_size;
                for (unsigned i = 0; i < num_blocks; ++i)
                  in.read(&heap[i * block_size], block_size);
                in.read(&heap[num_blocks * block_size], rest);
    

    vs.

    istreambuf_iterator<char> in_it (in.rdbuf());
                copy_n(in_it, heap.size(), heap.begin());
    

    vs.

    const size_t block_size = 1000000;
                size_t num_blocks = heap.size() / block_size;
                size_t rest = heap.size() - num_blocks * block_size;
                for (unsigned i = 0; i < num_blocks; ++i)
                  in.read(&heap[i * block_size], block_size);
                in.read(&heap[num_blocks * block_size], rest);
    

    Durchschnitt über 20 Durchläufe, jeweils 100000000 Bytes aus einer Datei, die zu der Zeit schon komplett im Festplattenpuffer steckte (Linux, ext4, GCC 4.6 -O3):

    Read, 4kB:
    User: 0.0425
    Sys: 0.0515
    Wall: 0.0922621
    
    istreambuf_iterator:
    User: 0.4085
    Sys: 0.054
    Wall: 0.462917
    
    Megabyteweise:
    User: 0.0015
    Sys: 0.093
    Wall: 0.0936295
    

    Alle Angaben in Sekunden.

    hustbaer gewinnt den Thread.


Anmelden zum Antworten