Geschwindigkeitsprobelm beim speichern daten in datei
-
Hallo,
folgendes:
Ich habe zuhause ein Programm für die Arbeit geschriebn, eine excel tabelle in der 3000 einträge drin sind in ein brauchbares csv format umgewandelt und diese dann zeilenweise eingelesen, in strings aufgeteilt und in einer list mit der entsprechenden struct geladen.Da das Programm aber öfter von mehreren rechner gestartet wird, muss er bei jeder Änderung die gesamte list in die datei schreiben und wieder hochladen...
Leider dauert genau das bei ca. 3000 einträgen sehr sehr lange, zumindest in der arbeit, daheim geht es fast zeitgleich mit mausklick...Hoffe die Problematik habe ich euch näher bringen können.
Wie würdet ihr solch ein Problem angehn? Datenbank fällt leider aus.Gruß
Mäxchen
-
Du müsstest deinen Quelltext zeigen, der für das Einlesen und Parsen der Datei verantwortlich ist, deiner Beschreibung nach denke ich, dass man am Algorithmus feilen muss. Jeder x-beliebige PC lädt eine Textdatei mit 3000 Zeilen schneller ein als man Blaubeerkuchen sagen kann, auch 20MHz 386er.
Liest du die Datei zeilenweise oder sogar zeichenweise ein? Du könntest die Datei erst komplett in den Hauptspeicher laden und dann parsen (falls du das nicht schon so machst).
-
Den Quellcode habe ich daheim, werde ich später hochladen...
im groben wird in meinem Programm folgendes gemacht:
lesen der Datei so:
bool MMI_Liste::OpenRead_File(string file_name, int &anzahl_datensaetze, list<MMI_Liste_Daten> &liste) { ifstream in_MMI(file_name.c_str()); zeile = 0; if(in_MMI) { while(!in_MMI.eof()) { getline(in_MMI, line_tmp); if(++zeile > 1) { MMI_Liste_Daten *FooBar = new MMI_Liste_Daten(); Filter_Line(line_tmp, FooBar ); liste.push_back (*FooBar); delete FooBar; } } anzahl_datensaetze = (int)liste.size(); return true; } else return false; }
filter bzw. trennen der datei:
void MMI_Liste::Filter_Line(string str_line, MMI_Liste_Daten *p) { if(str_line.find(';')!=string::npos) { size_t pos1 = str_line.find(';'); size_t pos2 = str_line.find(';',pos1+1); size_t pos3 = str_line.find(';',pos2+1); size_t pos4 = str_line.find(';',pos3+1); size_t pos5 = str_line.find(';',pos4+1); size_t pos6 = str_line.find(';',pos5+1); size_t pos7 = str_line.find(';',pos6+1); size_t pos8 = str_line.find(';',pos7+1); size_t pos9 = str_line.find(';',pos8+1); size_t pos10 = str_line.find(';',pos9+1); string trennen_finish; if(0 == pos1) trennen_finish = ""; else trennen_finish.append(str_line.begin(),str_line.begin()+pos1); p->finish = trennen_finish; string trennen_csa; if(pos1+1 == pos2) trennen_csa = ""; else trennen_csa.append(str_line.begin()+pos1+1,str_line.begin()+pos2); p->csa = trennen_csa; string trennen_csb; if(pos2+1 == pos3) trennen_csb = ""; else trennen_csb.append(str_line.begin()+pos2+1,str_line.begin()+pos3); p->csb = trennen_csb; string trennen_vorpr; if(pos3+1 == pos4) trennen_vorpr = ""; else trennen_vorpr.append(str_line.begin()+pos3+1,str_line.begin()+pos4); p->vorpr = trennen_vorpr; string trennen_endpr; if(pos4+1 == pos5) trennen_endpr = ""; else trennen_endpr.append(str_line.begin()+pos4+1,str_line.begin()+pos5); p->endpr = trennen_endpr; string trennen_datum; if(pos5+1 == pos6) trennen_datum = ""; else trennen_datum.append(str_line.begin()+pos5+1,str_line.begin()+pos6); p->datum = trennen_datum; string trennen_auftrag; if(pos6+1 == pos7) trennen_auftrag = ""; else trennen_auftrag.append(str_line.begin()+pos6+1,str_line.begin()+pos7); p->auftrag = trennen_auftrag; string trennen_matnr; if(pos7+1 == pos8) trennen_matnr = ""; else trennen_matnr.append(str_line.begin()+pos7+1,str_line.begin()+pos8); p->matnr = trennen_matnr; string trennen_stueckzahl; if(pos8+1 == pos9) trennen_stueckzahl = ""; else trennen_stueckzahl.append(str_line.begin()+pos8+1,str_line.begin()+pos9); p->stueckzahl = trennen_stueckzahl; string trennen_bemerkung; if(pos9+1 == pos10) trennen_bemerkung = ""; else trennen_bemerkung.append(str_line.begin()+pos9+1,str_line.begin()+pos10); p->bemerkung = trennen_bemerkung; string trennen_eingetragen_von; if(pos10+1 == str_line.length()) trennen_eingetragen_von = ""; else trennen_eingetragen_von.append(str_line.begin()+pos10+1,str_line.end()); p->eingetragen_von = trennen_eingetragen_von; } }
die dazugehörende struct:
struct MMI_Liste_Daten { string finish; string csa; string csb; string vorpr; string endpr; string datum; string auftrag; string matnr; string stueckzahl; string bemerkung; string eingetragen_von; };
alles einträge aus der dazugehörigen .h
Somit sind alle in der datei befindeten Einträge in der list nun enthalten.
Dann werden sie in einen commatext verwandelt und dieser dann in einen stringgrid ausgegeben.Dann zb. einen neuen eintrag mit list.push_back(eintrag); hinzufügen und sofort die komplette list die funktion Save_File, die ähnlich aufgebaut ist wie die open_file, schicken und komplett die neue Datei schreiben.
Alle Einträge werden hier geschrieben... und durch diese 3000 speichervorgänge dauert das ganze sehr sehr lange... bestimmt 5sec oder so...
-
Sieht ganz in Ordnung aus, das hätte ich nur drei Vorschläge zu machen:
-
lass das dynamische Erzeugen der Einträge weg und benutze eine lokale Variable. Dazu solltest du auch die Signatur der Filter_Line Methode ändern, sodass sie eine Referenz auf ein MMI_Liste_Daten akzeptiert.
-
Deklarier´ den Paramter str_line als const reference, dann muss keine Kopie erstellt werden.
-
Statt in der Filter_Line Methode mit lokalen Strings zu arbeiten, die dann den Attributen von MMI_List_Daten zugewiesen wird, kannst besser sofort auf die Attribute zugreifen:
while(!in_MMI.eof()) { getline(in_MMI, line_tmp); if(++zeile > 1) { MMI_Liste_Daten FooBar; Filter_Line(line_tmp, FooBar ); liste.push_back( FooBar ); } } void MMI_Liste::Filter_Line( const string& str_line, MMI_Liste_Daten& obj ) { ... if(0 == pos1) obj.finish = ""; else // statt append vielleicht assign? Sollte zwar performancemässig keinen // Unterschied machen, aber ist semantisch eindeutig obj.finis.append( str_line.begin(), str_line.begin()+pos1 ); }
-
-
Ohja, warum erstell ich da noch strings wenn ich die doch in der struct schon hab... gar nicht gesehn thx
Die anderen zwei Vorschläge werde ich auch beherzigen, dadruch wirds wieder ein klein wenig schneller...
Aber leider habe ich ja beim speichern das laaange Laden... nur in der Arbeit... hmmm
-
Hast du/deine Firma einen Profiler? Damit lassen sich Performanceengpässe gut aufspüren, vielleicht hakt´s ja an einer ganz anderen Stelle? Zur Not kannst du dir mit QueryPerformanceCounter und QueryPerformanceFrequency selbst eine hochpräzise Uhr bauen, mit der du die Ausführungszeit der Einlesefunktion messen kannst. Ich glaube nicht, dass das Einsparen einiger String Kopien einen spürbaren Geschwindigkeitsvorteil bringt.
-
DocShoe schrieb:
Hast du/deine Firma einen Profiler? Damit lassen sich Performanceengpässe gut aufspüren, vielleicht hakt´s ja an einer ganz anderen Stelle?
Auch meine Empfehlung. Am besten wäre natürlich ein Kaliber wie AQtime, aber für eine Kleinigkeit tut es auch Thomas Neumanns SampleProfiler (Standalone-Build hier).
-
So, ich denke ich weiß woran es liegt, bzw. leider nicht genau.
Aber da habt ihr bestimmt Erfahrung und könnt mir helfen.
Ich habe das Programm mit den Listen auf einen Laufwerk in unseren Hausnetz gespeichert, damit auch jeder darauf zugreifen kann.
Jedoch wenn ich das Programm auf C: lokal auf den Rechner zb. desktop kopiere und von dort aus starte funktioniert es so wie es soll, zeitnah per mausklick wird es auch in die liste eingetragen und in die datei gespeichert... juhuu!
Aber.. ich muss ja die listen auf den Hausnetz legen, damit auch jeder darauf zugreifen kann.
Woran kann das jetzt genau liegen? bzw. was kann ich dagegen tun?
Danke, mfg
PS: Habe mir das mit der Zeitmessung angeschaut und werde dies auch nutzen um die Funktionen zu optimieren thx
-
Mäxchen schrieb:
Jedoch wenn ich das Programm auf C: lokal auf den Rechner zb. desktop kopiere und von dort aus starte funktioniert es so wie es soll, zeitnah per mausklick wird es auch in die liste eingetragen und in die datei gespeichert... juhuu!
Liegen bei dieser Gegenprobe auch die zu bearbeitenden Listen auf dem lokalen Rechner, oder sind sie weiterhin auf dem Server und werden über das Netz geladen bzw. gespeichert?
-
Also sobald ich die Listen lokal abspeicher funktioniert das speichern in einer klasse geschwindigkeit, aber sobald die Dateien dann im Netzwerk liegen und dort gespeichert werden dauert es ewig lang...
Wo das Programm selbst gespeichert ist, ist egal...
Wie würdet ihr vorgehn?
Danke. Gruß
Mäxchen
-
Wie lange dauert denn das einfache kopieren der Datei aufs Netzwerk?
-
ofstream out("datei.txt); out<<inhalt;
ich speicher so im groben ab, und das dauert 3,3sec.
Wenn ich lokal speichere, dauert es gerade mal 0,02sec
-
Der Ansatz selbst ist schon zum Scheitern verurteilt. Wenn mehrere Benutzer (darum liegt die Datei wohl auf einem Netzlaufwerk) schreibend auf eine Datei zugreifen ist es nur eine Frage der Zeit, bis entweder die Datei kaputt ist oder Konflikte auftreten. Es mag zwar selten auftreten, aber es wird irgendwann auftreten. Nach Murphy treten solche Fehler immer am unglücklichsten Zeitpunkt auf.
Was ist passiert in folgendem Szenario:
- Benutzer A lädt die Datei lokal in den Speicher seines Computers
- Benutzer B lädt die Datei lokal in den Speicher seines Computers
- Benutzer A ändert Zeilen 5 und 6 und schreibt dann die Datei neu
- Benutzer B ändert Zeile 3 und schreibt die Datei dann neu=> Änderungen des Benutzers A gehen verloren, weil Benutzer B von diesen Änderungen nichts mitbekommen hat
=> VersionskonfliktDu brauchst auf jeden Fall irgendeine Art von Zugriffssynchronisierung, im einfachsten Fall vielleicht so etwas wie alleinigen Schreibzugriff auf die Datei zusammen mit der Kennzeichnung neuer Daten. Für das obige Beispiel könnte das folgendermassen aussehen:
- exklusiver Zugriff auf die Datei für den Schreibmodus
- Statusfeld für jeden Eintrag (dirty/clean). Damit erkennst du, welche Einträge geändert worden sind und welche ihren alten Inhalt behalten. Beim Schreiben der Daten ersetzt du dann lediglich die Einträge, die mit dirty markiert sind, alle anderen werden aus der Datei übernommen. Das löst die Versionskonflikte, wenn mehrere Benutzer gleichzeitig Änderungen an der Datei vorgenommen haben und so nur die geänderten Daten geschrieben werden, anstatt die komplette Datei zu überschreiben.
Am besten automatisierst du das Lesen/Ändern/Schreiben durch eine Test Unit, die in unregelmässigen Abständen die Datei liest, ändert und zurückschreibt. Dann startest du mehrere Instanzen, lässt das mal über Nacht/Wochenende laufen und guckst dir das Ergebnis an.
-
Ich habe mir das so gedacht, ich lege eine Datei an, wo mit 1 und 0 beschrieben ist, ob irgendeine Liste geändert wurde und wenn ja, welche. Während das Programm dann läuft, soll abgefragt werden und dann eventuell die neue Liste einlesen.
Quasi wird das dann abgefragt, bei re aktivieren vom programm, oder kurz bevor man speichern will.
So ähnlich habe ich mir das vorgestellt.
Wenn mehrere offen sind wird gespeichert wieviel quasi online sind.
zB: es sind 4 online und einer hat eine Datei geändert, dann kommt der code 13, quasi steht die 1 dann für Datei verändert, und die 3 für die anzhal die noch einen refresh machen müssen. Wenn dann einer neu einliest ändert er den code auf 12, zum schlus dann 00, und somit ist jedes Programm auf den aktuellen Stand...Aber wenn ich mir das recht überlege, wie soll ich das handhaben wenn 2 gleichzeitig etwas ändern...oh weh...
Wie realisier ich das wohl am einfachsten...
Das mit den unregelmäßigen einlesen und ändern, mit dirty und clear ist eine gute Lösung, aber doch sehr aufwändig.!?
Aber ich würde noch immer ganz gern wissen warum das speichern auf dem netzlaufwerk sooo extrem lange dauert!??
Danke. Gruß
Mäxchen
-
Hallo
Du könntest dir nun ein Programm schreiben, das aktiv auf dem Server (dort wo jetzt deine Datei liegt) den Zugriff auf die Daten für die Clients verwaltet. Oder einfach eine vorhandene Datenbanklösung wie MySQL, Postgre oder Firebird verwenden, denn genau dazu sind Datenbanken da.
bis bald
akari
-
Ja, mit Hilfe einer Datenbank wäre das ganze wohl um einiges einfacher..
Aber leider bin ich nicht von der IT und die werden mir auch keine Berechtigung geben..
also löse ich das mit einer "status.dat".
-
Da beisst sich doch die Katze selbst in den Schwanz. Für die Status.dat existieren doch die gleichen Probleme wie für die eigentlichen Daten.
Dieser Ansatz für das Schreiben sollte doch funktionieren:
- Datei im Exklusiv-Schreibmodus öffnen
- falls nicht erfolgreich, kurze Pause, zurück zu 1), ggf. abbrechen
- Dateiinhalt einlesen
- alle Zeilen, die im Speicher als dirty markiert sind, in die frisch eingelesenen Daten übertragen
- geänderten Daten schreiben und Datei schliessen
-
Nachtrag:
Es gibt auch noch so Sachen wie embedded, self contained Datenbanksysteme (z.B. SQLite oder HamsterDB), die keine Installation eines DBMS voraussetzen sondern als DLL oder statische Bibliothek daherkommen und gegen die Anwendung gelinkt werden. Allerdings kenne ich keine, die prozessübergreifende Synchronisation unterstützt.
-
Warum es so langsam ist: die Datei wird häppchenweise angefordert und nach jeder Anforderung muss auf die Antwort gewartet werden.
Also erst die Datei komplett einlesen, und erst dann verarbeiten.
Beim Schreiben genauso, erst alle Daten vorbereiten und dann alle aufs Mal schreiben (bzw. in Blöcken bei sehr großen Dateien).
-
Ok, Idee:
Da ja beim Schreiben die 3 sec zustande kommen, würde ich dann die komplette Datei lokal in irgendein temp verzeichnis speichern und anschließen mit einer c++ funktion komplett ins netz kopiere. Ist das ein möglicher Lösungsweg?
Mit welcher Funktion kann ich die Datei dann kopieren? bzw. Verschieben?
@DocShoe
Ich weiß genau was du meinst. aber da
das Programm höchstens von 5 Benutzer gestartet wird und diese höchstens jeder 10 Einträge an einem Tag eintragen werden ist das nicht sooo schlimm.
Das kleine Risiko geh ich ein.zudem wird ja, bevor ein Schreibvorgang beginnt ein Status auf 1(in status.dat) gesetzt, der danach wieder auf 0 gesetzt wird. Somit weiß ein anderes Programm das es nicht schreiben kann bzw. die liste dann nochmal neu einlesen muss und dann die änderung vom 2. Programm speichern muss.
Danke. Gruß
Mäxchen