Schreiben in Ascii optimieren
-
Na ja, streng genommen ist das doppelte, man schreibt ja auch.
Ich finde es trotzdem egal. Ich würde nach wie vor pro Lese-Datei einen ifstream anlegen. Und meine Variante erfordert ja auch, dass man pro ifstream einen ofstream mit ios::app anlegt. Dann hat man also immer nur eine Datei auf einmal im Speicher. Das wird die Performance nicht wirklich drücken.
Und das in einen stringstream hauen bedeutet, dass man den gesamten Kopiervorgang verdoppelt. Anstatt alles "auf dem Weg" zu lesen, kopiert man es in den Speicher und parst dann dort wiederum durch. Ich bin nicht sicher, ob man dadurch irgendwelche Page/Cache-Misses einschränkt... aber das Performance-Problem mit operator<</>> liegt einfach darin, dass für jeden Vorgang ein sentry-Objekt angelegt wird, das teure Dinge macht, siehe den von Werner verlinkten Thread, hier verlinkt mit dem Post, in dem er es begründet: http://www.c-plusplus.net/forum/p2267872#2267872
Legt man nur zwei sentrys (pro istream/ostream) pro Datei-Parse-Vorgang an, boosted man die Performance also automatisch. Denn sputc macht außer eof-Abfrage und Schauen, ob der Buffer noch ok ist, eigentlich nichts außer das Zeichen in den Buffer zu schreiben, jedenfalls in meiner MSVC-Implementierung.
Nächste Frage ist vermutlich, ob die Facette zum Parsen von double schnell genug ist. Falls von Werner keine grundsätzlichen Einwände kommen und OP daran Interesse hätte, weil er noch nichts Besseres fand, versuche ich das später vielleicht auch Mal.

-
Hey also erstmal danke für die schöne Diskusion, blicke aber noch nicht wirklich durch, ob es jetzt schneller geht

Bzgl. SSD - habe es gerade auf SSD ausprobiert und es dauert genauso lang

Bin leider auch noch in meinen C++ Anfängen.
Grüße
Sorry hatte die 2. Seite nicht gesehen:
Hierzu: Das Programm sollte unter root von Cern laufen, daher kann ich leider keine stringsteams benutzen, da das irgendwie nicht unterstützt wird. Ist es den so viel schneller als Stream? Dann würde ich halt doch ein Standalone bauen.
-
Was heißt "irgendwie nicht unterstützt"? ostream/istream funktionieren, stringstream aber nicht? sstream hast Du aber inkludiert? Was ist denn die Fehlermeldung?
-
hey,
also das problem ist, wenn ich das program mit stringstreams ausführe in Visual Studio, funktionierte es problemlos. Habe den code dann kopiert und in der Uni mit root gestartet, und es hat nicht funktioniert. Habe es daraufhin in sprintf umgeschrieben und schon gehts. (Zuhause Windows in der Uni Linux) Fehlermeldung an weiß ich nicht. Irgendwas mit Can't open ....!
-
Can't open or load the PDB-file?
-
Hey,
also habs gerade mal mit einem anderen Code getestet:
Code:
#include <fstream> #include <iostream> #include <vector> // #include "H1.h" /* wird fuer root-Histogramme benoetigt */ // #include "TFile.h" /* wird fuer root-Histogramme benoetigt */ // #include <TTree.h> /* wird fuer root-Trees benoetigt */ //#include "TBranch.h" // #include "TBasket.h" #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <iostream> #include <fstream> #include <cstdlib> #include <string> #include <math.h> #include <sstream> #include <TH2.h> #include <TStyle.h> // #include<TTree.h> int main() { double a,b,c,Channel; std::stringstream filename,channelnumber; filename<<"run00029_ch"; //Eintragen des Ladeverzeichnisses in der Form "%%\\run00001_ch std::cout << "Welcher Channel soll geplottet werden? \n" ; std::cin >> Channel; channelnumber << filename.str() << Channel; std::vector<double> x; std::vector<double> y; std::vector<double> t; std::ifstream myFile; myFile.open (channelnumber.str()); // -----------------------root Tree erstellen char rootTreeName[60]; char* rohdatname="Testmessung"; sprintf(rootTreeName,"%s.channel35.root",rohdatname); // Name fuer Root Tree-Datei festlegen TFile *hfile = new TFile(rootTreeName,"RECREATE"); TTree *tree = new TTree("MyTree","An example of a ROOT tree"); // Tree erstellen tree->Branch("sg", &b ,"b/D"); tree->Branch("lg", &c, "c/D"); //------------------------- root Tree füllen for (int i=1; i<1000000; i++) { double a=0,b,c; myFile>>a>>b>>c; x.push_back(b); y.push_back(c); t.push_back(a); if (a<1) {break;} tree->Fill(); } myFile.close(); hfile->Write(); hfile->Close(); }Fehler ist:
Error: Can't call basic_ifstream<char,char_traits<char> >::open(channelnumber.str()) in current scope test.cpp:39:
Possible candidates are...
public: void basic_ifstream<char,char_traits<char> >::open(const char* s,ios_base::openmode mode=ios_base::in);Im compiler geht es aber

-
Du musst zum Öffnen der Datei vor C++11 einen C-String übergeben (*.str().c_str()).
Der andere Compiler hatte offenbar C++11, dort geht es auch mit einem std::string.
-
wie mache ich das?
PC auf dem es geht Windows8wo es nicht geht:
Ubuntu 12.10
-
Eisflamme schrieb:
War das bezugnehmend auf meinen Post?
Nein, aber dein code wird trotzdem lahm sein, weil er dn ganzen locale-overhad mitnimmt.
template<typename E_in, typename Traits_in, typename E_out, typename Traits_out> void fastCopy(basic_istream<E_in, Traits_in>& in, basic_ostream<E_out, Traits_out>& out) { out << in.rdbuf(); }Was ich machen würde: datei in einn string einlsen (mittels std::stringbuf) dann den string parsen und, die Werte umwandeln, wieder in einen string umwandeln und dann als block an die Ausgabedatei anhängen.
-
Okay, aber der Witz an der Sache ist dann ja: Wie parst Du den String dann?
-
Lummel202 schrieb:
wie mache ich das?
PC auf dem es geht Windows8wo es nicht geht:
Ubuntu 12.10Hänge einfach ein .c_str() hinten an.
-
void evenFasterCopy(std::string cconst& inPath, std::string const& outPath) { boost::iostreams::mapped_file_source in(inPath); boost::iostreams::mapped_file_sink out(outPath, in.size()); if(!in.is_open() || !out.is_open()) return; std::memcpy(out.data(), in.data(), in.size()); }Wenn ich nichts übersehen habe.
-
Eisflamme schrieb:
Okay, aber der Witz an der Sache ist dann ja: Wie parst Du den String dann?
da gibts so einige Möglichkeiten. Die 3-Zeilen Lösung wäre in diesem Fall spirit::qi. Hab ich gemacht, top performance, bin hoch zufrieden.
-
Hi,
Ethon:
Und wie willst Du das jetzt zu einem Parser erweitern?Also ich habe das Mal kurz weitergebaut. Der jetzige Parser liest den ersten double, führt die Konvertierungsfunktion aus, gibt den konvertierten Wert aus und schreibt den Rest der Zeile in die Datei (wobei er ' ' durch '\t' ersetzt).
Ich muss zugeben, das Parsen und Schreiben vom double ist das Bottleneck (wäre es das nicht aber sowieso immer?). 140 MB konvertieren bei mir jedenfalls in 18 Sekunden, wobei man dazu sagen muss, dass meine Festplatte gerade auch ordentlich zu tun hat. Weiß nicht, wie sehr das Auswirkungen hat. Rechnet man das gemäß Werners Annahme hoch, dürfte das ganze Prozedere also ungefähr 2 Minuten dauern.
Findest Du das lahm, otze? Wie schnell könnte man mit einem "richtigen Parser" denn werden?
#include <iostream> #include <sstream> #include <fstream> #include <ctime> #include <iterator> using namespace std; const double t_offset12 = 0.12; const double t_offset13 = 0.13; const double t_offset14 = 0.14; const double t_offset15 = 0.15; // whatever offsets are double getTimeStamp(double a, unsigned int fileNumber) { double timestamp; if(fileNumber / 8 < 1) timestamp = a; if(fileNumber / 8 < 2 && fileNumber / 8 > 0.99) timestamp = a + t_offset12; if(fileNumber / 8 < 3 && fileNumber / 8 > 1.99) timestamp = a + t_offset13; if(fileNumber / 8 < 4 && fileNumber / 8 > 2.99) timestamp = a + t_offset14; if(fileNumber / 8 < 5 && fileNumber / 8 > 3.99) timestamp = a + t_offset15; if(fileNumber / 8 < 6 && fileNumber / 8 > 4.99) timestamp = a + t_offset12; else timestamp = 0.; return timestamp; } template<typename E_in, typename Traits_in, typename E_out, typename Traits_out> void parse(basic_istream<E_in, Traits_in>& in, basic_ostream<E_out, Traits_out>& out, unsigned int fileNumber) { typedef basic_istream<E_in, Traits_in> istream_t; typedef basic_ostream<E_out, Traits_out> ostream_t; istream_t::sentry ok_in(in); ostream_t::sentry ok_out(out); if(ok_in && ok_out) { ios_base::iostate state_in = ios_base::goodbit; ios_base::iostate state_out = ios_base::goodbit; try { typedef istream_t::_Iter in_it; typedef num_get<E_in, in_it> numget; typedef ostream_t::_Iter out_it; typedef num_put<E_out, out_it> numput; const numget& getter = use_facet<numget>(in.getloc()); const numput& putter = use_facet<numput>(out.getloc()); for(;;) { // read double and write it double d; getter.get(in_it(in.rdbuf()), in_it(0), in, state_in, d); putter.put(out_it(out.rdbuf()), out, out.fill(), getTimeStamp(d, fileNumber)); // copy rest of line while converting ' ' to '\t' Traits_in::int_type m; char c; while(!Traits_in::eq_int_type(m = in.rdbuf()->sbumpc(), Traits_in::eof())) { c = Traits_in::to_char_type(m); if(c == ' ') c = '\t'; out.rdbuf()->sputc(c); if(m == '\n') break; } if(Traits_in::eq_int_type(m, Traits_in::eof())) break; } } catch(...) // unschön, aber ich konnte nicht feststellen, dass sbumpc() nicht throwen kann { state_in |= ios_base::badbit; if(in.exceptions() & ios_base::badbit) throw; state_out |= ios_base::badbit; if(out.exceptions() & ios_base::badbit) throw; } in.setstate(state_in); out.setstate(state_out); } } // =================================================== int main() { time_t start = time(0); { ifstream ifs("C:/someInput.txt", ios::binary); ofstream ofs("C:/someOutput.txt", ios::binary); ofs.precision(15); parse(ifs, ofs, 1); cout << "Time difference: " << difftime(time(0), start) << " seconds"; } int x; cin >> x; }Edit XY:
Und "\t\t", i am Zeilenende fehlen natürlich noch...
-
Kein Kommentar? Gerne auch Kritik von Werner (geht das so in Ordnung oder habe ich irgendwas vergessen? Vermutlich müsste ich noch die States wenigstens einmal pro Schleife oder so prüfen?), eine bessere non-stream-Lösung oder eine Rückmeldung von OP, ob er das weiterbauen kann.

-
Hab gerade selber nicht viel Zeit. Abeer du kannst ja mal nen spirit parser dagegen anlaufen lassen.
-
Ethon:
Und wie willst Du das jetzt zu einem Parser erweitern?Die anderen Copy-Vorschläge hier hatten auch nicht viel mit Parsern zu tun.
Aber: Ich würde einen Streambuf schreiben der auf den Memory-Mapped files operiert (bzw ich gehe stark davon aus dass es den in Boost scchon gibt, scchließlich sind die MemorymappedFiles ein Teil von Boost.Iostreams).Damit spart man sich einiges an Rumkopiererei im Speicher und überlässt dem OS die Entscheidung, was jetzt im Speicher liegt oder auf der Platte bleibt.
-
Hallo Lummel,
vor einiger Zeit bin ich auf diese Diskussion gestoßen und fand das Ergebnis ganz interessant. Das zweite Beispiel ist zwar nicht mehr C++, schau dir aber noch den Rest an. Du hast hier schon einige gute Vorschläge bekommen, aber vielleicht hilft's dir weiter.
-
Werner Salomon schrieb:
Hallo Eisflamme,
Eisflamme schrieb:
Werner:
Wäre hier nicht wieder ein guter Trick statt operator>> eine eigene Version zu schreiben, die nur ein einzelnes sentry-Objekt pro Lesevorgang aus der Datei erzeugt? In deiner Version hast Du ja auch ein sentry pro Zeile. Könnte man nicht sogar eine Funktion schreiben, die gleichzeitig für Output und Input ein sentry erzeugt und die Zeichen dann gar nicht zwischenspeichert, sondern den sget() direkt sput()ted?Vielleicht ist das für OP zu viel und es geht hier auch nicht um meine Frage, aber in die Richtung hätte ich jetzt gedacht.
Ich weiß schon auf welchen Thread Du Bezug nimmst. Na ja - mach' Du mal, ich gehe jetzt in's Bett.
Ansonsten - beachte den Kommentar zu meinem eigenen Beitrag.
Gutes Nächtle
WernerWas ich ziemlich interessant fand: http://www.soi.ch/phpBB3/viewtopic.php?p=2120#p2120
Es ist nicht nur das Sentry, was langsam ist, sondern der ganze Parser der Streams.