Schreiben in Ascii optimieren
-
Ich weiß nicht wie gut diese ganzen low-level-System-Aufrufe sind, aber vielleicht beschleunigt sich das ganze, wenn man die komplette Datei zuerst in den Speicher ließt, einen stringstream draus macht und auf dem dann arbeitet... Und das Ergebnis ebenfalls einen stringstream schreibt. Kann mir vorstellen, dass durch das einsparen der ganzen read/write-Aufrufe einiges an Performance herauszuholen ist.
-
Genau, man hantiert mit 47 x 20MB im Arbeitsspeicher herum...
-
Das macht nichtmal 1gb. Das ist heutzutage doch kein Problem mehr.
-
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.