Benutzen von 'rdbuf()'
-
Also da ihr ja genauso neugierig seid, wie ich, dann hier mal der komplette Quellcode. Und zwar inklusive der vorgeschlagenen Buffer-Variante mit einem 8k buffer, die genauso schnell ist, wie Variante 4 und 5:
#include <iostream> #include <fstream> #include <iterator> #include <cxxtools/arg.h> int main(int argc, char* argv[]) { try { cxxtools::Arg<int> variant(argc, argv, 'v', 1); for (int a = 1; a < argc; ++a) { std::ifstream file(argv[a]); switch (variant.getValue()) { case 1: std::copy( std::istream_iterator<char>(file>>std::noskipws), (std::istream_iterator<char>()), std::ostream_iterator<char>(std::cout) ); break; case 2: std::copy( std::istream_iterator<char>(file>>std::noskipws), (std::istream_iterator<char>()), std::ostreambuf_iterator<char>(std::cout) ); break; case 3: std::copy( std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>(), std::ostream_iterator<char>(std::cout)); break; case 4: std::copy( std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(std::cout)); break; case 5: std::cout << file.rdbuf(); break; case 6: { char buffer[8192]; while (file.read(buffer, sizeof(buffer)), file.gcount() > 0) std::cout.write(buffer, file.gcount()); break; } } } } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } }
Compiliert habe ich das mit:
g++ -o cat -O2 cat.cpp
Testen kann ich dann mit:
time ./cat -v 1 grosseDatei|md5sum
Oder auch alle Varianten in einem Rutsch:
for v in 1 2 3 4 5 6; do time ./cat -v $v grosseDatei|md5sum; done
Also es gibt keinen Quellcode für die Zeitmessung. Das macht time für mich. Na ja, klar, GNU/Linux ist Open-Source. Irgendwo gibt es die Sourcen zum time Befehl, aber den brauche ich hier sicher nicht zu posten (ach wie ich Linux doch liebe
)
cxxtools::Arg ist eine Klasse zum extrahieren von Kommandozeilenoptionen, die bei mir in praktisch jedem Programm vor kommt, da sie so praktisch ist.
-
Guten Mittag,
hier noch ein Programm für Windows-Systeme, das die Geschwindigkeit aller 6 Varianten misst.test.txt
ist eine 100 MB große Textdatei.Meine Gegebenheiten:
`i7-860 2.80GHz (8 CPUs)
8 GB RAM
Windows 7 Prof. 64-Bit
Visual Studio 2008 Prof.
Optimierung: /O2
`
Quellcode:
#include <iostream> #include <iterator> #include <fstream> #include <algorithm> #include <windows.h> #include <time.h> using namespace std; int main() { LONGLONG frequency = 0; LONGLONG start = 0; LONGLONG ende = 0; double elapsed_variante_1 = 0; double elapsed_variante_2 = 0; double elapsed_variante_3 = 0; double elapsed_variante_4 = 0; double elapsed_variante_5 = 0; double elapsed_variante_6 = 0; if( !QueryPerformanceFrequency((LARGE_INTEGER*)&frequency) ) return -1; // Variante 1: istream_iterator + ostream_iterator { ifstream file("test.txt"); ofstream out("variante_1.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); copy( istream_iterator<char>(file>>noskipws), (istream_iterator<char>()), ostream_iterator<char>(out) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_1 = (((double)(ende-start))/((double)frequency)); } // Variante 2: istream_iterator + ostreambuf_iterator { ifstream file("test.txt"); ofstream out("variante_2.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); copy( istream_iterator<char>(file>>noskipws), (istream_iterator<char>()), ostreambuf_iterator<char>(out) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_2 = (((double)(ende-start))/((double)frequency)); } // Variante 3: istreambuf_iterator + ostream_iterator { ifstream file("test.txt"); ofstream out("variante_3.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); copy( istreambuf_iterator<char>(file), istreambuf_iterator<char>(), ostream_iterator<char>(out) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_3 = (((double)(ende-start))/((double)frequency)); } // Variante 4: istreambuf_iterator + ostreambuf_iterator { ifstream file("test.txt"); ofstream out("variante_4.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); copy( istreambuf_iterator<char>(file), istreambuf_iterator<char>(), ostreambuf_iterator<char>(out) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_4 = (((double)(ende-start))/((double)frequency)); } // Variante 5: einfach rdbuf { ifstream file("test.txt"); ofstream out("variante_5.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); out << file.rdbuf(); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_5 = (((double)(ende-start))/((double)frequency)); } // Variante 6: read + write (8192 Byte Blöcke) { ifstream file("test.txt"); ofstream out("variante_6.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); char buffer[8192]; while( file.read(buffer, sizeof(buffer)), file.gcount()>0 ) { out.write(buffer, file.gcount()); } QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_6 = (((double)(ende-start))/((double)frequency)); } cout << endl << "elapsed_variante_1 = " << elapsed_variante_1; // 17,78 Sekunden cout << endl << "elapsed_variante_2 = " << elapsed_variante_2; // 8,97 Sekunden cout << endl << "elapsed_variante_3 = " << elapsed_variante_3; // 10,45 Sekunden cout << endl << "elapsed_variante_4 = " << elapsed_variante_4; // 2,58 Sekunden cout << endl << "elapsed_variante_5 = " << elapsed_variante_5; // 2,53 Sekunden cout << endl << "elapsed_variante_6 = " << elapsed_variante_6; // 0,83 Sekunden }
Messergebnisse:
`Variante 1: istream_iterator + ostream_iterator = 17,78 Sekunden
Variante 2: istream_iterator + ostreambuf_iterator = 8,97 Sekunden
Variante 3: istreambuf_iterator + ostream_iterator = 10,45 Sekunden
Variante 4: istreambuf_iterator + ostreambuf_iterator = 2,58 Sekunden
Variante 5: einfach rdbuf = 2,53 Sekunden
Variante 6: read + write (8192 Byte Blöcke) = 0,83 Sekunden
`
Erkenntnisse:
- stream_iterator sind extrem langsam.
- streambuf_iterator und rdbuf sind praktisch gleich schnell.
- Blockweises kopieren ist am Schnellsten.
-
Was ich auch interessant finde, ist, dass das Kopieren des Dateiinhalts in einen
std::vector
viastd::copy
schneller ist, als viastd::vector
-Konstruktor:Quellcode:
#include <iostream> #include <iterator> #include <fstream> #include <algorithm> #include <windows.h> #include <time.h> #include <vector> using namespace std; int main() { LONGLONG frequency = 0; LONGLONG start = 0; LONGLONG ende = 0; double elapsed_variante_1 = 0; double elapsed_variante_2 = 0; double elapsed_variante_3 = 0; double elapsed_variante_4 = 0; if( !QueryPerformanceFrequency((LARGE_INTEGER*)&frequency) ) return -1; // Variante 1: istream_iterator + constructor { ifstream file("test.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v( istream_iterator<char>(file>>noskipws), (istream_iterator<char>()) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_1 = (((double)(ende-start))/((double)frequency)); } // Variante 2: istream_iterator + copy { ifstream file("test.txt"); ofstream out("variante_2.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v; copy( istream_iterator<char>(file>>noskipws), (istream_iterator<char>()), back_inserter(v) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_2 = (((double)(ende-start))/((double)frequency)); } // Variante 3: istreambuf_iterator + constructor { ifstream file("test.txt"); ofstream out("variante_3.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v( istreambuf_iterator<char>(file), (istreambuf_iterator<char>()) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_3 = (((double)(ende-start))/((double)frequency)); } // Variante 4: istreambuf_iterator + copy { ifstream file("test.txt"); ofstream out("variante_4.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v; copy( istreambuf_iterator<char>(file), (istreambuf_iterator<char>()), back_inserter(v) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_4 = (((double)(ende-start))/((double)frequency)); } cout << endl << "elapsed_variante_1 = " << elapsed_variante_1; // 9,11 Sekunden cout << endl << "elapsed_variante_2 = " << elapsed_variante_2; // 8,17 Sekunden cout << endl << "elapsed_variante_3 = " << elapsed_variante_3; // 2,76 Sekunden cout << endl << "elapsed_variante_4 = " << elapsed_variante_4; // 2,04 Sekunden }
Messergebnisse:
`Variante 1: istream_iterator + constructor = 9,11 Sekunden
Variante 2: istream_iterator + copy = 8,17 Sekunden
Variante 3: istreambuf_iterator + constructor = 2,76 Sekunden
Variante 4: istreambuf_iterator + copy = 2,04 Sekunden
`
-
out schrieb:
Was ich auch interessant finde, ist, dass das Kopieren des Dateiinhalts in einen
std::vector
viastd::copy
schneller ist, als viastd::vector
-Konstruktor:Das ist doch sehr ungewöhnlich. Hast du auch alle Optimierungen an (auch die berüchtigten STL-Range Checks von Visual Studio ausgeschaltet?) und bei mehrfachen Messungen das gleiche Ergebnis erhalten? Auch wenn du die Reihenfolge der Messungen umdrehst?
-
SeppJ schrieb:
out schrieb:
Was ich auch interessant finde, ist, dass das Kopieren des Dateiinhalts in einen
std::vector
viastd::copy
schneller ist, als viastd::vector
-Konstruktor:Das ist doch sehr ungewöhnlich. Hast du auch alle Optimierungen an (auch die berüchtigten STL-Range Checks von Visual Studio ausgeschaltet?) und bei mehrfachen Messungen das gleiche Ergebnis erhalten? Auch wenn du die Reihenfolge der Messungen umdrehst?
Jap. Ich habe mit der Optimierung
/Ox (alles optimieren)
kompiliert, die Messungen umgedreht und nun auchchecked iterators
deaktiviert (falls du das gemeint hast?). Die Ergebnisse sind gleich geblieben, auch nach mehrmaliger Durchführung.#include <iostream> #include <iterator> #include <fstream> #include <algorithm> #include <windows.h> #include <time.h> #include <vector> using namespace std; #define _SECURE_SCL 0 // disable checked iterators int main() { LONGLONG frequency = 0; LONGLONG start = 0; LONGLONG ende = 0; double elapsed_variante_1 = 0; double elapsed_variante_2 = 0; double elapsed_variante_3 = 0; double elapsed_variante_4 = 0; if( !QueryPerformanceFrequency((LARGE_INTEGER*)&frequency) ) return -1; // Variante 2: istream_iterator + copy { ifstream file("test.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v; copy( istream_iterator<char>(file>>noskipws), (istream_iterator<char>()), back_inserter(v) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_2 = (((double)(ende-start))/((double)frequency)); } // Variante 1: istream_iterator + constructor { ifstream file("test.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v( istream_iterator<char>(file>>noskipws), (istream_iterator<char>()) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_1 = (((double)(ende-start))/((double)frequency)); } // Variante 4: istreambuf_iterator + copy { ifstream file("test.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v; copy( istreambuf_iterator<char>(file), (istreambuf_iterator<char>()), back_inserter(v) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_4 = (((double)(ende-start))/((double)frequency)); } // Variante 3: istreambuf_iterator + constructor { ifstream file("test.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v( istreambuf_iterator<char>(file), (istreambuf_iterator<char>()) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_3 = (((double)(ende-start))/((double)frequency)); } cout << endl << "elapsed_variante_1 = " << elapsed_variante_1; // 8,93 Sekunden cout << endl << "elapsed_variante_2 = " << elapsed_variante_2; // 8,27 Sekunden cout << endl << "elapsed_variante_3 = " << elapsed_variante_3; // 2,76 Sekunden cout << endl << "elapsed_variante_4 = " << elapsed_variante_4; // 2,05 Sekunden }
-
Wäre nett, wenn es jemand auf einem Linux-System nachprüfgen könnte.
-
#include <iostream> #include <iterator> #include <fstream> #include <algorithm> #include <vector> #ifdef _WIN32 # include <Windows.h> #else # include <sys/time.h> # include <unistd.h> #endif using namespace std; namespace { #ifndef _WIN32 typedef long long LONGLONG; typedef long long LARGE_INTEGER; int QueryPerformanceFrequency(LARGE_INTEGER *f) { *f = 1000; return 1; } void QueryPerformanceCounter(LARGE_INTEGER *c) { struct timespec ts = {0}; clock_gettime(CLOCK_MONOTONIC, &ts); *c = static_cast<LONGLONG>(ts.tv_sec) * 1000 + static_cast<LONGLONG>(ts.tv_nsec) / 1000 / 1000; } #endif bool ensureTestFile(const std::string &name, size_t size) { { std::ifstream file(name, std::ios::ate); if (file && (file.tellg() == size)) { return true; } } std::ofstream file(name, std::ios::binary); static const char content[1UL << 14] = {}; if (!file) { return false; } for (size_t i = 0; i < size; ) { file.write(content, std::min<size_t>(sizeof(content), (size - i))); i += sizeof(content); } return true; } } int main() { static const auto Mebi = (1UL << 20); if (!ensureTestFile("test.txt", 100 * Mebi)) { return 1; } LONGLONG frequency = 0; LONGLONG start = 0; LONGLONG ende = 0; double elapsed_variante_1 = 0; double elapsed_variante_2 = 0; double elapsed_variante_3 = 0; double elapsed_variante_4 = 0; if( !QueryPerformanceFrequency((LARGE_INTEGER*)&frequency) ) return -1; // Variante 2: istream_iterator + copy { ifstream file("test.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v; copy( istream_iterator<char>(file>>noskipws), (istream_iterator<char>()), back_inserter(v) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_2 = (((double)(ende-start))/((double)frequency)); } // Variante 1: istream_iterator + constructor { ifstream file("test.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v( istream_iterator<char>(file>>noskipws), (istream_iterator<char>()) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_1 = (((double)(ende-start))/((double)frequency)); } // Variante 4: istreambuf_iterator + copy { ifstream file("test.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v; copy( istreambuf_iterator<char>(file), (istreambuf_iterator<char>()), back_inserter(v) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_4 = (((double)(ende-start))/((double)frequency)); } // Variante 3: istreambuf_iterator + constructor { ifstream file("test.txt"); QueryPerformanceCounter((LARGE_INTEGER*)&start); vector<char> v( istreambuf_iterator<char>(file), (istreambuf_iterator<char>()) ); QueryPerformanceCounter((LARGE_INTEGER*)&ende); elapsed_variante_3 = (((double)(ende-start))/((double)frequency)); } cout << "elapsed_variante_1 = " << elapsed_variante_1 << endl; cout << "elapsed_variante_2 = " << elapsed_variante_2 << endl; cout << "elapsed_variante_3 = " << elapsed_variante_3 << endl; cout << "elapsed_variante_4 = " << elapsed_variante_4 << endl; }
$ /usr/lib/gcc-snapshot/bin/g++ --version g++ (Ubuntu/Linaro 20120314-0ubuntu2) 4.8.0 20120314 (experimental) [trunk revision 185382] [...] $ /usr/lib/gcc-snapshot/bin/g++ main.cpp -omain -std=c++0x -lrt -O3 $ ./main elapsed_variante_1 = 3.372 elapsed_variante_2 = 3.446 elapsed_variante_3 = 0.922 elapsed_variante_4 = 0.905
-
Danke TyRoXx. Unterm Strich kommt es also praktisch auf dasselbe heraus. Trotzdem interessant, finde ich. Hätte eher gedacht, dass
std::vector
-Konstruktor ein bisschen deutlicher vorne liegen würde.
-
out schrieb:
if( !QueryPerformanceFrequency((LARGE_INTEGER*)&frequency) )
Was soll dieser Müll-Cast denn hier?
Willst damit wohl eine Compilerwarnung zudecken, was glaubst du wohl, warum der Compiler die Warnung ausgibt?
Richtig! Weil der Compiler Ahnung hat im Gegensatz zu dir. Zeigercasts sind prinzipiell schlecht und deuten allermeistens auf fehlerhaftes Programmdesign hin.
-
out schrieb:
Meine Gegebenheiten:
`i7-860 2.80GHz (8 CPUs)
8 GB RAM
Windows 7 Prof. 64-Bit
Visual Studio 2008 Prof.
Optimierung: /O2
`
Messergebnisse:
`Variante 1: istream_iterator + ostream_iterator = 17,78 Sekunden
Variante 2: istream_iterator + ostreambuf_iterator = 8,97 Sekunden
Variante 3: istreambuf_iterator + ostream_iterator = 10,45 Sekunden
Variante 4: istreambuf_iterator + ostreambuf_iterator = 2,58 Sekunden
Variante 5: einfach rdbuf = 2,53 Sekunden
Variante 6: read + write (8192 Byte Blöcke) = 0,83 Sekunden
`
Lahme Krücke
`elapsed_variante_1 = 10.3109elapsed_variante_2 = 4.85142
elapsed_variante_3 = 6.86091
elapsed_variante_4 = 1.5387
elapsed_variante_5 = 1.3171
elapsed_variante_6 = 0.427581`
-
Swordfish schrieb:
out schrieb:
Meine Gegebenheiten:
`i7-860 2.80GHz (8 CPUs)
8 GB RAM
Windows 7 Prof. 64-Bit
Visual Studio 2008 Prof.
Optimierung: /O2
`
Messergebnisse:
`Variante 1: istream_iterator + ostream_iterator = 17,78 Sekunden
Variante 2: istream_iterator + ostreambuf_iterator = 8,97 Sekunden
Variante 3: istreambuf_iterator + ostream_iterator = 10,45 Sekunden
Variante 4: istreambuf_iterator + ostreambuf_iterator = 2,58 Sekunden
Variante 5: einfach rdbuf = 2,53 Sekunden
Variante 6: read + write (8192 Byte Blöcke) = 0,83 Sekunden
`
Lahme Krücke
`elapsed_variante_1 = 10.3109elapsed_variante_2 = 4.85142
elapsed_variante_3 = 6.86091
elapsed_variante_4 = 1.5387
elapsed_variante_5 = 1.3171
elapsed_variante_6 = 0.427581`
Hier sind meine Ergebnisse:
elapsed_variante_1 = 4.313
elapsed_variante_2 = 1.719
elapsed_variante_3 = 3.135
elapsed_variante_4 = 0.092
elapsed_variante_5 = 0.091
elapsed_variante_6 = 0.083
Warum ist bei mir die schnellste Variante um den Faktor 5 schneller, als Deine schnellste?Übrigens: Intel i5, Fedora 17, x64_64 auf Intel i5.
Ist Linux an der Stelle jetzt so viel schneller?
-
Du musst das relativ sehen. Caching, Festplattengeschwindigkeit etc machen einen großen Unterschied.
-
Was passiert wenn man Variante 6 als erstes testet? Caching könnte die Ergebnisse verfälschen.
-
Jep. Eigentlich müsste mn ja mit nem strinngstream testen.
-
Ethon schrieb:
Du musst das relativ sehen. Caching, Festplattengeschwindigkeit etc machen einen großen Unterschied.
Caching von was? Hat meine Festplatte ein besseres Caching oder Linux? Genau das ist ja die Frage.
Ich verweise ja auf den Beitrag von Swordfish, der meint, sein System sei keine lahme Krücke. Da frage ich mich halt, ob meine Festplatte so gigantisch gut ist oder mein Betriebssystem.
Und noch zu der Frage von Oberon_O: Hier sind die Ergebnisse, wenn ich Test 6 an den Anfang verschiebe:
elapsed_variante_1 = 4.045
elapsed_variante_2 = 1.559
elapsed_variante_3 = 2.699
elapsed_variante_4 = 0.091
elapsed_variante_5 = 0.09
elapsed_variante_6 = 0.081Also nein - es macht nichts aus.
-
Caching von was? Hat meine Festplatte ein besseres Caching oder Linux? Genau das ist ja die Frage.
Afaik cacht Liux beträchtlich mehr. Bei Linux beschwert sich ja niemand wenn das OS nach dem Start mal eben 4GB RAM verwendet.
-
Ethon schrieb:
Jep. Eigentlich müsste mn ja mit nem strinngstream testen.
Hier meine Ergebnisse mit ostringstream:
elapsed_variante_1 = 3.498
elapsed_variante_2 = 1.545
elapsed_variante_3 = 2.407
elapsed_variante_4 = 0.077
elapsed_variante_5 = 0.077
elapsed_variante_6 = 0.075
-
Ethon schrieb:
Jep. Eigentlich müsste mn ja mit nem strinngstream testen.
Eigentlich nicht. Es geht ja um das blockweise kopieren.
-
stirni schrieb:
Ethon schrieb:
Jep. Eigentlich müsste mn ja mit nem strinngstream testen.
Eigentlich nicht. Es geht ja um das blockweise kopieren.
Höh? Es geht doch um die Performance der verschiedenen Methoden um Daten von Stream a nach Stream b zu kopieren.
-
Ethon schrieb:
stirni schrieb:
Ethon schrieb:
Jep. Eigentlich müsste mn ja mit nem strinngstream testen.
Eigentlich nicht. Es geht ja um das blockweise kopieren.
Höh? Es geht doch um die Performance der verschiedenen Methoden um Daten von Stream a nach Stream b zu kopieren.
Ach ich habe eher das Gefühl, wir probieren mal dies und mal das. Und ich finde das sehr spannend
.
Wenn es nur um das kopieren eines Streams in einen anderen ginge, sollten wir eher mal einfach von einem std::istringstream in einen std::ostringstream kopieren. Sollten wir das mal ausprobieren?