Ich habe verschiedene std::vector Instanzen mit numerischen Daten in ihnen, vor allem int16_t, int32_t, etc. Ich möchte diese Daten so schnell wie möglich in eine Datei ausgeben. Wenn ich einen ostream_iterator verwende, wird er den gesamten Speicherblock in einer einzigen Operation schreiben, oder wird er die Elemente des Vektors durchlaufen und für jedes Element eine Schreiboperation ausführen?
Antworten
Zu viele Anzeigen?Der schnellste (aber schrecklichste) Weg, einen Vektor zu entladen, ist, ihn in einer Operation mit ostream::write zu schreiben:
os.write( (char *) &v[0], v.size() * sizeof( value_type) );
Sie können dies mit einer Vorlagenfunktion etwas schöner gestalten:
template <typename T>
std::ostream & DumpVec( std::ostream & os, const std::vector <T> & v ) {
return os.write( &v[0], v.size() * sizeof( T ) );
}
die es Ihnen ermöglicht, Dinge zu sagen wie:
vector <unsigned int> v;
ofstream f( "file.dat" );
...
DumpVec( f, v );
Das Zurücklesen wird etwas problematisch sein, es sei denn, man stellt dem Schreiben irgendwie die Größe des Vektors voran (oder die Vektoren haben eine feste Größe), und selbst dann wird man auf verschiedenen Endian- und/oder 32- bzw. 64-Bit-Architekturen Probleme haben, wie mehrere Leute bereits erwähnt haben.
Ein Stream-Iterator und ein Vektor werden in keiner mir bekannten Implementierung eine Blockkopie verwenden. Wäre der Vektorelementtyp beispielsweise eine Klasse und nicht ein POD, wäre eine direkte Kopie eine schlechte Sache. Ich vermute, dass der ostream die Ausgabe auch formatieren wird, anstatt die Werte direkt zu schreiben (d.h. ascii statt binärer Ausgabe).
Vielleicht haben Sie mehr Glück mit boost::copy
da es speziell dafür optimiert ist, Blockschreibvorgänge durchzuführen, wenn dies möglich ist, aber die praktischste Lösung besteht darin, den Vektorspeicher direkt zu bearbeiten, indem man &v[0]
.
Die meisten ofstream
Implementierungen, die ich kenne, puffern Daten, so dass Sie wahrscheinlich nicht am Ende eine übermäßige Anzahl von Schreibvorgängen durchführen. Der Puffer in der ofstream()
muss sich füllen, bevor ein tatsächlicher Schreibvorgang durchgeführt wird, und die meisten Betriebssysteme puffern die Dateidaten auch darunter. Das Zusammenspiel dieser Faktoren ist auf der Ebene der C++-Anwendung keineswegs transparent; die Auswahl der Puffergrößen usw. bleibt der Implementierung überlassen.
C++ bietet eine Möglichkeit, einen eigenen Puffer an eine ostream
's Streambuf . Sie können versuchen, anzurufen pubsetbuf
wie diese:
char *mybuffer = new char[bufsize];
os.rdbuf()->pubsetbuf(mybuffer, bufsize);
Der Nachteil ist, dass dies nicht unbedingt etwas bringt. Einige Implementierungen Ignorieren Sie es einfach .
Die andere Möglichkeit, die Sie haben, wenn Sie Dinge zwischenspeichern wollen und trotzdem die ostream_iterator
ist die Verwendung eines ostringstream
, z.B.:
ostringstream buffered_chars;
copy(data.begin(), data.end(), ostream_iterator<char>(buffered_chars, " ");
string buffer(buffered_chars.str());
Sobald alle Daten gepuffert sind, können Sie den gesamten Puffer mit einer großen ostream::write()
POSIX E/A, usw.
Dies kann jedoch immer noch langsam sein, da Sie eine formatierte Ausgabe vornehmen und zwei Kopien Ihrer Daten gleichzeitig im Speicher haben müssen: die Rohdaten und die formatierten, gepufferten Daten. Wenn Ihre Anwendung bereits an die Grenzen des Speichers stößt, ist dies nicht der beste Weg, und Sie sind wahrscheinlich besser dran, wenn Sie die eingebaute Pufferung verwenden, die ofstream
gibt Ihnen.
Wenn Sie wirklich Leistung wollen, ist der schnellste Weg, dies zu tun, die Auslagerung des Rohspeichers auf die Festplatte mit ostream::write()
als Neil schlägt vor oder die E/A-Funktionen Ihres Betriebssystems zu verwenden. Der Nachteil dabei ist, dass Ihre Daten nicht formatiert sind, Ihre Datei wahrscheinlich nicht von Menschen lesbar ist und auf Architekturen mit einer anderen Endianness als der, von der aus Sie geschrieben haben, nicht leicht lesbar ist. Aber die Daten werden schnell und ohne zusätzlichen Speicherbedarf für Ihre Anwendung auf die Festplatte übertragen.
Wenn Sie den ostream_iterator mit einem ofstream konstruieren, wird sichergestellt, dass die Ausgabe gepuffert wird:
ofstream ofs("file.txt");
ostream_iterator<int> osi(ofs, ", ");
copy(v.begin(), v.end(), osi);
das ofstream-Objekt ist gepuffert, so dass alles, was in den Stream geschrieben wird, gepuffert wird, bevor es auf die Festplatte geschrieben wird.
- See previous answers
- Weitere Antworten anzeigen