Datei laden und parsen (Optimierung)
-
Erstmal solltest du statt der char*-Version von getline() lieber die string-Version verwenden:
getline(f,tmp_str);Und zweitens kannst du mit find_first_of() überprüfen, ob du ein Zeichen im String hast:
getline(f,str); stringstream wandler(str); if(str.find_first_of(" \t") { //mehrere Werte wandler>>w1>>w2>>w3; if(!wandler) { cout<<"Fehler"; break; } //... } else { //einzelner Wert wandler>>w0; //... }
-
Die frage ist, wie schnell du es wirklich brauchst ....
gib mal ne Groessenordnung der Datei ?

muss es plattformunabhaengig sein ?
wenn nein, schau dir memorymapping an ... Datei-Block in speicher schieben, und dann mit zeigern draufrumrutschen ^^Fuer das parsen selber solltest keine Stringobjecte neu anlegen, sondern nur mit zeigerarithmethik suchen
Fuer das wegspeichern der Tokens und werte dann kannst scho strings erstellen (std::string) Das spart dir kopien beim reinen parsen ....Ich wuerd mir aber erst mal an deiner stelle nen Interface fuer deinen Parser bauen, und die langsame aber sicher funktionierende variante mit lesen in die std::string Puffer nehmen ...
Wenn dann alles ordentlich funktioniert, kannst das parsen nach oben benannter methode vielleicht immer noch beschleunigen. Vielleicht wenn sauber programmiert hasst, brauchst es ja gar nicht mehr ^^Prinzipiell ...
Deine dateien scheinen eklig gross werden zu koennen, also viele Daten, aber die Struktur selber ist recht klein und uebersichtlich ....
Also bietet sich nen parser mit eventschnittstelle grade zu an ....
Damit trennst du das parsen von der datenhaltung und wirst noch flexiebler.
Du brauchst aigentlich nur wenige events, alsovirtual void OnStart() = 0;
virtual void OnStop() = 0;
virtual void OnMessreihe(unsigned int iNr) = 0;
virtual void OnOffset(unsigned int iNr, unsigned int iOffset) = 0;
virtual void OnMeasurmentPoint(unsigned int iNr,double iValue1, double iValue 2) = 0;Sollte als Interface fast langen ....
Dem Parser uebergibts nen Object mit der Schnittstelle und der befeuert dir das dann ...
Aehnlich arbeiter SAX zum beispiel, und sowas iss eigentlich schon performant ^^Ciao ...
-
Würde so etwas versuchen. So musst du den string nicht direkt überprüfen.
void read_file( istream & str ) { int nr; double mreal = 0.0; double mimag = 0.0; double r; string line; while ( getline( str, line ) ) { stringstream sstr(line); int cou = 0; sstr >> nr; while ( sstr >> r ) { switch ( cou ) { case 0: mreal = r; break; case 1: mimag = r; break; default : cout << "error parsing '" << line << "'" << endl; break; } cou++; } if ( cou ) { cout << "datensatz " << nr << ":" << mreal << "," << mimag << endl; } else { cout << "neue Messreihe " << nr << endl; } } }Kurt
-
Fuer den ersten entwurf und um sein programm erstmal zum laufen zu bringen, voellig ok.
Aber steckt auch noch bisserl optimierungspotential drinne .void read_file( istream & str ) { int nr; double mreal = 0.0; double mimag = 0.0; double r; string line; while ( getline( str, line ) ) { // Die komplette Zeile wird kopiert, nach paarmal ist der string hoffentlich gross genug ^^ stringstream sstr(line); // Der string wird gleich in nen object umgebaut, und dazu noch total neu konstruiert, sehr sicher, aber auch sehr rechenintensiv ^^ int cou = 0; sstr >> nr; // tolle idee, die std streamoperationen gleich zu nutzen. while ( sstr >> r ) { switch ( cou ) { case 0: mreal = r; break; case 1: mimag = r; break; default : cout << "error parsing '" << line << "'" << endl; break; } cou++; } if ( cou ) { cout << "datensatz " << nr << ":" << mreal << "," << mimag << endl; } else { cout << "neue Messreihe " << nr << endl; } } }Wie gesagt, fuer kleinere Sachen sicher akzeptabel, fuer zig millionen mal aufrufen sicher optimierungsbeduerftig ... und je nach implementation der STL wirst hier sehr unterschiedliche ergebnisse erzielen ....
Punkte wo man ansetzen kann ...
1. deine zeilen sind eigentlich definiert max lang, zumindest die Puffer (std::string) von haus aus gross genug Konstruieren ....
2. bei der sofortigen kopie das neuerstellen des stringstreams verhindern, sondern in den puffer des alten schreiben lassen ... damit hilfst der STL nen new(aufn eigenen allokator zwar) zu vermeiden. Bei guten impls wirds nich viel ausmachen, weil die allokatoren recht flott sind
Wenn das immer noch zu langsam ist, wirst dem mit reinen C++ mitteln kaum noch zu leibe ruecken koennen.
Was die sache "langsam" macht, sind die X-maligen kopien ...eine variante, waere c++ klassen zu verwenden, die cow (copy on write) koennen (nicht mehr STL, dafuer unsicherer, code bleibt aber lesbar. faellt mir auf anhieb die MFC und die QT ein)
oder auf selber in die hand nehmen und der sache mit C-Funktionen und roher pointerarithmetic auf die pelle ruecken.Ciao ...
-
Habs ein wenig optimiert. Jetzt wird nicht mehr so viel kopiert.
void read_file( istream & str ) { int nr; double mreal = 0.0; double mimag = 0.0; double r; stringstream sstr; char buffer[100]; while ( str.getline( buffer, 100 ) ) { sstr.str(buffer); int cou = 0; sstr >> nr; while ( sstr >> r ) { switch ( cou ) { case 0: mreal = r; break; case 1: mimag = r; break; default : cout << "error parsing '" << buffer << "'" << endl; break; } cou++; } if ( cou ) { cout << "datensatz " << nr << ":" << mreal << "," << mimag << endl; } else { cout << "neue Messreihe " << nr << endl; } sstr.clear(); } }Mein erstes Posting sollte eigentlich nur die Idee zeigen dass man die überprüfung der Eingabezeile auch den streamfunktionen überlassen kann.
Kurt
-
Jo er soll mal die standard ausgabe auskommentieren und laufen lassen ...
wenns dann zufriedenstellend schnell durchlaeuft, hat er doch was er braucht ^^Ciao ...
-
DANKE DANKE DANKE DANKE.
Da sind wirklich tolle Ideen dabei und ich habe jetzt auch eine Idee wie ich das Umsetzen kann.@RHBaum
Wegen der Frage nach der Größenordnung: Im Moment ist das ganze noch off-line, also ist die Zeit noch relativ unkritisch. Es sollen aber später Messreihen mit 16k-Samples pro Sekunde gelesen werden.
-
Du bekommst dann deine daten nich mehr per file sondern per Stream ?
oder per Net I/O ?nich das die geschichte mit dem File dann zum umweg wird ?
glaub nich dass nen Messystem in ne datei reinstreamt und sich drauf verlaesst dass du konkurrierend zugreifend den muell selber da rausraeumst ^^
also rein vom IO wenns sauber gemacht ist, iss 16kbyte / s eher unkritisch , da codier ich ja noch mp3's im hinnergrund bei ^^
ich mach hier teilweisse mit 1000kbaud rum, ok, aber ich krieg meine daten auch per shared memory und mit events ... aber das parsen sollte da nich so das prob sein. In nen temp file schreiben schon eher, weil das zeitverhalten unter nichtechtzeit BS nicht definiert istCiao ...
-
Das wird dann später zweigleisig.
Aktuelle Daten per Stream und da das Messsystem die Daten in Dateien ablegt benötige ich das Laden ja auch, um ältere Aufnahmen zu betrachten.
-
daishi schrieb:
Das wird dann später zweigleisig.
Aktuelle Daten per Stream und da das Messsystem die Daten in Dateien ablegt benötige ich das Laden ja auch, um ältere Aufnahmen zu betrachten.In dem Fall solltest du auf jeden Fall die Stream-Version verwenden (andernfalls benötigst du zwei gesonderte Funktionen für's einlesen vom Netzwerk (vermutlich über ein istream-Derivat) und Datei (FILE*).
-
Das system die daten in so Textformate als log wegspeichern is durchaus ueblich
Das messysteme die daten in streams in der selben form irgendwo rausstreamen, iss eher unueblich ^^
meist haben die systeme ne API (C-Header + dll) wo ma die daten pollen muss, oder noch besser per event benachrichtigt wird ...
die daten liegen dann meist in schoenen C-Structs vor, andernfalls wuerd ich dem hersteller an die Gurgel springen ^^
In dem Fall wuerd ich mir zur uebertragung nen format(protokoll) suchen(bauen) wo ich nimmer "parsen" muesst ^^Naja, ich kenn aber nich alles was es aufm Markt gibt, waer aber ziemlich ungewoehlich ....
Ciao ...
-
hier mal trick 17 mit selbst überlistung:
void read_file( istream & str ) { //zwischenspeicher float array[4]; int index=0; while(str){ str>>array[index]; ++index; if(index==4){ //test //wenn eine neue messreihe anfängt, hat das array folgende form: //messreihenNummer|0|zahl|0 //es gibt keine messwertkonstellation, bei denen die beiden //nullen an genau diesen stellen auftauchen könnten. //wenn der erste wert keine messreihenNummer ist, so geht der //datensatz von element 0-2. element 3 gibt dann entweder die //nummer einer neuen messreihe, oder den ersten wert eines //neuen datensatzes an. Da messreihen laut dateischnipsel mit 1 beginnen, und datensätze mit einer 2, bleibt nur der offsetwert übrig. if(array[3]==0){ messreihenNummer=array[0]; data1=array[1]; data2=array[2]; data3=array[3]; //index zurücksetzen, array komplett ausgewertet index=0; } else { //kein offset wert. //die ersten 3 daten bilden einen messwert data1=array[0]; data2=array[1]; data3=array[2]; //der 4. wert wurde "zu früh" gelesen, und wird an den anfang gesetzt array[0]=array[3]; //da der erste wert gültig ist, index nicht auf 0 sondern 1 setzen index=1; } } } }//edit mom ich teste das eben schnell :p
//edit2 der testschnipsel vom threadstarter funzt. Kriegts jemand noch schneller? jetzt wär ich mal so richtig gespannt^^