Warum ist mein code so langsam?
-
Muss ne Datei mit 60000 Einträgen der Form
n;number;tid;vs;date
auslesen. (Ne einfache csv-Datei halt.) Aber mein Code braucht für 100 Einträge schon fast 20 sec. (2Ghz Pentium M)
Wie kann ich das beschleunigen? Kanns sein dass die Verwendung der stl den Code so langsam macht?Code:
bool TReverseDatabase::LoadFromFile (string filename) { string in; string line; string number, tid, vs, date, n; string separator= ";", linefeed = "\r\n"; string::iterator linebegin, lineend, linex; in = LoadStringFromFile (filename); if (in.length () == 0) return false; else { linebegin = in.begin (); while (linebegin != in.end ()) { n.clear (); number.clear (); tid.clear (); vs.clear (); date.clear (); lineend = search (linebegin, in.end (), linefeed.begin (), linefeed.end ()); linex = search (linebegin, lineend, separator.begin (), separator.end ()); n.insert (norm.end (), linebegin, linex); linebegin = linex+1; linex = search (linebegin, lineend, separator.begin (), separator.end ()); //typ.insert (norm.end (), linebegin, linex); linebegin = linex+1; linex = search (linebegin, lineend, separator.begin (), separator.end ()); number.insert (number.end (), linebegin, linex); linebegin = linex+1; linex = search (linebegin, lineend, separator.begin (), separator.end ()); tid.insert (tid.end (), linebegin, linex); linebegin = linex+1; linex = search (linebegin, lineend, separator.begin (), separator.end ()); vs.insert (vs.end (), linebegin, linex); linebegin = linex+1; date.insert (date.end (), linebegin, lineend); linebegin = lineend+2; AddEntry (number, tid, vs, date, n); } } return true; }Danke für Antworten!
-
warum nicht so?
std::ifstream inputFileStream("input.csv"); std::string line; while(std::getline(inputFileStream, line)) { // "line" splitten }
-
Dein Dateieinlesen ist nicht so der Hammer, mit nem fstream und getline ist das praktikabler (und erspart dir einiges an unnützem Suchaufwand).
void TReverseDatabase::LoadFromFile (std::string filename) { std::string line; std::string el[5]; const char separator= ';'; std::ifstream in (filename.c_str ()); while (getline (in, line)) { std::string::size_type begin = 0, end; for (int i = 0; i < 5; ++i) { end = line.find (separator, begin + 1); el[i].swap (line.substr (begin, end)); begin = end; } AddEntry (el[0], e[1], el[2], el[3], el[4]); } }Du kannst noch Abfragen reinmachen und ggf. Exceptions werfen.
-
Hi .filmor, könntest du mir den Sinn von swap dadrin erklären.
Blicke gerade nicht durch wieso nen swap statt ne einfach zuweisung...

-
Nuja, das erspart (hoffentlich) eine unnötige Kopie. swap tauscht die Daten zweier Strings aus. substr liefert ein neues string-Objekt zurück. Die alten Daten werden nicht mehr benötigt und können somit ruhig zusammen mit dem temporären zurückgegebenen Objekt ins Nirvana gehen, während die neuen so ohne Verdopplung der Daten benutzt werden.
/edit Schon klar, ist nicht unbedingt lesbarer, aber er wollte Speeeed ...

-
Nunja, habe immer noch nicht ganz durchgeblickt...
Die alten Daten werden nicht mehr benötigt
Welche alten Daten ???
Ich habe soviel verstanden.substr gibt ja ein temp Objekt zurück. Temp kommt in das Array und das was im Array war (das ist ja "nichts" drin) ins temp. Daraufhin wird temp ja zerstört.
Kann es nicht sein das temp vorher zerstört wird bevor swap da was reinschreibt ???
Bei einem el[i] = line.substr(..) wird doch der temporäre-String in el geschrieben und dann zerstört.
Sehe leider immernoch irgendwie keinen Vorteil dadrin.
Musst es mir wohl ausführlicher erklären
-
Mkay, nochmal in Einzelschritten (dass das Ziel in einem Array liegt ist völlig unerheblich, deshalb heißt e[ i ] jetzt target!):
1. Fall:
target = line.substr (begin, end);- Es wird ein neues Stringobjekt ohne Namen (ab jetzt Temp) mit dem Inhalt des Bereiches von begin bis end in line erstellt
- Dieser String wird nun zeichenweise nach target geschrieben (das ist allerdings implementierungsabhängig, später mehr)
- Jetzt wird Temp destruiert und geht mitsamt seinen Daten unter. target enthält nun eine Kopie
2. Fall:
target.swap (line.substr (begin, end));- Erster Schritt wie oben
- Nun tauschen die beiden Strings ihre Daten. Da die eigentlich immer Heap-allokiert sind reicht ein Tauschen des Zeigers aus (3-mal schreiben, vgl. oben)
- Letzter Schritt wie oben, allerdings enthält Temp nur noch unwichtigen Datenmüll, der es verdient entsorgt zu werden
So, nun eine Anmerkung: Bei einigen (wohl kaum threadsicheren) Implementierungen von std::string (wie z.B. in der GNU libstdc++) wird das Copy-on-Write-Verfahren verwendet, das mit Referenzzählung arbeitet.
In einem solchen Fall hat hier auch der erste Fall konstante Laufzeit ganz ähnlich der beim zweiten, da die Daten erst bei einer Veränderung kopiert werden. Das ist aber ein Implementierungsdetail der Standardbibliothek und, wie gesagt, AFAIK kaum threadsicher zu bekommen. Der 2. Fall hingegen ist mit praktisch jeder Implementierung gleich schnell./edit: Argh, kann man irgendwie BBCode escapen?!
-
Ok danke, habs verstanden

-
N00Bie schrieb:
Muss ne Datei mit 60000 Einträgen der Form
n;number;tid;vs;date
auslesen. (Ne einfache csv-Datei halt.) Aber mein Code braucht für 100 Einträge schon fast 20 sec. (2Ghz Pentium M)
Wie kann ich das beschleunigen? Kanns sein dass die Verwendung der stl den Code so langsam macht?Nein, Nein Nein.
Die Verwendung der STL macht gar nichts langsam. Ich möchte wissen wer dieses hartnäckige und falsche Gerücht in die Welt gesetzt hat. Es begegnet mir zu oft.
Nur die falsche Verwendung von irgendwas - wie z.B. + oder for oder einem Function-Call oder auch von std::string - machen Code langsam ... manchmal sehr langsam.Folgende Lösung benötigt für das Einlesen einer Datei mit 60000 Einträgen, so wie oben beschrieben, ca 1 Sekunde (in Worten: "eine"). 2 Ghz PC mit schneller Festplatte

#include <iostream> #include <fstream> #include <algorithm> #include <iterator> // istream_iterator<> #include <string> struct Record { std::string m_n; std::string m_number; std::string m_tid; std::string m_vs; std::string m_date; }; std::istream& operator>>( std::istream& in, Record& rec ) { const char SEP = ';'; getline( in, rec.m_n, SEP ); getline( in, rec.m_number, SEP ); getline( in, rec.m_tid, SEP ); getline( in, rec.m_vs, SEP ); getline( in, rec.m_date ); // endet mit Zeilenende return in; } struct Nil { Nil& operator*() { return *this; } Nil& operator++() { return *this; } Nil& operator=( const Record& rec ) { // hier Deinen Code einfügen, der 'rec' abspeichert return *this; } }; int main() { using namespace std; ifstream file( "Big.csv" ); copy( istream_iterator< Record >( file ), istream_iterator< Record >(), Nil() ); return 0; }Gruß
Werner
-
.filmor schrieb:
Nuja, das erspart (hoffentlich) eine unnötige Kopie. swap tauscht die Daten zweier Strings aus. substr liefert ein neues string-Objekt zurück. Die alten Daten werden nicht mehr benötigt und können somit ruhig zusammen mit dem temporären zurückgegebenen Objekt ins Nirvana gehen, während die neuen so ohne Verdopplung der Daten benutzt werden.
Wieso einfach, wenn's umständlich geht?

target.assign(begin, end);
-
Wenn schon, dann bitte so:
target.assign (line, begin, end);Aber hast Recht, das ist besser.
-
Nope. Was soll das denn sein? Die Iteratoren werden ja wohl ihr Objekt, bzw. die entsprechenden Daten, kennen. Dein Aufruf hat jedenfalls nichts mit den STL Spezifikationen zu tun.
-
Hat er wohl :p.
Falls du mal in meinen Code schaust siehst du, dass ich die find-Methode von string und nicht die find-Funktion aus algorithm verwendet habe. Die find-Methode liefert keine Iteratoren sondern std::string::size_type zurück (womit begin und end ja auch deklariert sind), deshalb muss der Quellstring mit angegeben werden.
-
Ja ok, deinen Code mit find habe ich nicht angeschaut. Verwende in Zukunft andere Namen, dann gibts auch keine Missverständnisse. Wie wär's zB mit pos und n? So wie es in der Funktionssignatur bei assign der Fall ist? begin und end werden jedenfalls für gewöhnlich in Verbindung mit Iteratoren verwendet. Deshalb ging ich davon aus, dass das auch hier der Fall ist. Zumindest end ist jedenfalls ziemlich irreführend.