Laden...

Großen String (500KB) schnell speichern dauert ca. 1 Minute

Erstellt von Red-Sh4nks vor 7 Jahren Letzter Beitrag vor 7 Jahren 3.152 Views
R
Red-Sh4nks Themenstarter:in
47 Beiträge seit 2011
vor 7 Jahren
Großen String (500KB) schnell speichern dauert ca. 1 Minute

Hallo geschätzte mycsharp-Community!

Ich stehe vor einem Speicherproblem. Gegeben ist relativ großer String, welcher abgespeichert in einer Textdatei eine Größe von ungefähr 0,5 MB besitzt. Wenn ich diesen String nun mittels


            StreamWriter myFile = new StreamWriter(newPath);
            myFile.Write(Speicher);
            myFile.Write(
            myFile.Close();

speichern will, so benötigt das Programm ca. 1 Minute dafür. Auch


System.IO.File.WriteAllText(newPath, Speicher);

beschleunigt das Ganze nicht. Jetzt meine Frage: Kennt jemand eine Möglichkeit, wie dieser Vorgang beschleunigt werden kann?

Anmerkungen:*Ich habe mit Google keine Lösung gefunden *Ich habe auch im Forum keine Lösung gefunden *Diese Funktion wird in kurzen Zeitabständen aufgerufen. Threads sind daher keine Option

16.842 Beiträge seit 2008
vor 7 Jahren

Ich glaube nicht, dass es an diesem Codestück liegt.
Das braucht bei mir mit einem simulierten String nur wenige ms.

[Tutorial] Vertrackte Fehler durch Vergleich von echtem Projekt mit minimalem Testprojekt finden

Ein String mit 500 KB ist nicht unbedingt groß.
Deine Aussage zufolge hättest Du eine Schreibgeschwindigkeit von 0.0083 MB/s. Bei mir liegt die bei 527MB/s.
Jedes Handy hat höhere Schreibgeschwindigkeiten als 0.0083 MB/s 😉

3.003 Beiträge seit 2006
vor 7 Jahren

Zur Untermauerung von Abt, kurzer Test mit File.WriteAllText:


500000000 bytes written in 00:00:02.6045965

Also eine 1000mal längere Zeichenkette als deine in zweieinhalb Sekunden. Dein Problem liegt definitiv woanders.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

W
872 Beiträge seit 2005
vor 7 Jahren

Erstmal solltest Du Dich mit using/dispose vom StreamWriter befassen.
Zweitens solltest Du Dich mit Anforderungen beschäftigen - warum musst Du ständig Strings abspeichern - kannst Du vielleicht mit Deltas arbeiten. Das Du sagst daß Threads keine Lösung sind, verstehe ich auch nicht. Gerade bei IO sind Threads/Tasks oft die Lösung, damit das Schreiben nicht alles blockiert.

R
Red-Sh4nks Themenstarter:in
47 Beiträge seit 2011
vor 7 Jahren

Vielen Dank für die Antworten.

@Abt und LaTino:
Ihr habt recht. Ich habe jetzt ebenfalls einen randomgenerierten String verwendet und siehe da: Das Problem ist verschwunden. Darauf habe untersucht wie die Daten zusammengetragen. Dabei bin ich auf eine For-Schleife mit ~400^2 Durchläufen gestoßen. Beim Stoppen eines Speichervorgangs konnten dort 50 Sekunden Rechenzeit nachgewiesen werden. Deswegen muss ich jetzt diese Schleife irgendwie beschleunigen. (parallel.foreach fällt, fürchte ich, schon mal weg, da die Daten chronologisch gespeichert werden müssen)

@weismat: In diesen 500KB stecken die Daten die regelmäßig von verschieden Klasseninstanzen zusammengetragen (in Abständen von ~10 Sekunden) und als Backup gespeichert werden müssen, damit bei einem Absturz kein Fortschritt verloren gehen kann (Verlauf muss aufgezeichnet werden). Wenn alle 10 Sekunden ein Thread eröffnet wird, der 60 Sekunden Abwicklungszeit benötigt, wird das Programm früher oder später terminieren. Deinen ersten Ratschlag werde ich mir zu Herzen nehmen.

D
985 Beiträge seit 2014
vor 7 Jahren

Kann es sein, dass du den Status von allen Klassen immer wieder komplett ermittelst und schreibst?

Da liegt der Fehler.

1.040 Beiträge seit 2007
vor 7 Jahren

In solchen Fällen sollte man immer genau eingrenzen, wo die Zeit tatsächlich verloren geht, um nicht auf die falsche Fährte (hier Schreiben der Datei) gelockt zu werden. 😉

Wenn die Funktion "SammleDatenUndSpeicherSie" eine Minute benötigt und darin vor dem Speichern der Datei erst noch die benötigten Daten gesammelt werden, packt man erst um das Sammeln der Daten seine Stoppuhr (oder was auch immer) und schaut wie schnell das ist - ist es schnell, prüft man den zweiten Block etc.
Hat man einen Block als Zeitfresser identifiziert arbeitet man sich innerhalb des Blocks weiter vor usw. =)

D
985 Beiträge seit 2014
vor 7 Jahren

@p!lle

Obwohl, wenn ich das Schreiben im Verdacht habe, dann messe ich das Schreiben (aber auch nur das). Meistens liegt der Zeitfresser allerdings nicht beim Schreiben 8)

1.040 Beiträge seit 2007
vor 7 Jahren

@Sir Rufo:
Dann wäre doch aber aufgefallen, dass das Schreiben fix geht. 👅
Natürlich gibt es Codeteile, die man direkt "in Verdacht" hat, die kann man auch gerne zuerst messen, allerdings stellt sich dann ja sofort raus, ob sie es sind oder nicht.

16.842 Beiträge seit 2008
vor 7 Jahren

Paralleles Schreiben auf Festplatten ist total sinnfrei. Nur weil etwas parallel ist heisst das nicht, dass es schneller wäre.
Im Fall von HDDs ist das in 100% der Fälle auch kontraproduktiv.

Wenn alle 10 Sekunden ein Thread eröffnet wird, der 60 Sekunden Abwicklungszeit benötigt, wird das Programm früher oder später terminieren.

Das hat genau 0 mit Threads zutun.
Das wäre dann mit einem einfachen Locking zu bewerkstelligen, dass keine neue Abarbeitung startet, solange eine aktuelle noch läuft.
Das ist Anwendungslogik.

R
Red-Sh4nks Themenstarter:in
47 Beiträge seit 2011
vor 7 Jahren

Ja, wie gesagt ich hatte beim Sammeln der Daten diese exzessive Schleife übersehen und deswegen von vornherein das Speichern als Zeitfresser im Verdacht. Deswegen habe ich nach Lösungen dazu gesucht. Ich habe mich geirrt 😃 Das Thema sollte damit erledigt sein.

@Abt:

Das wäre dann mit einem einfachen Locking zu bewerkstelligen, dass keine neue Abarbeitung startet, solange eine aktuelle noch läuft.

Ich bin weit nicht so erfahren wie du, deswegen verzeih mir bitte diese laienhafte Frage: Wenn ich deine suggerierte Methode anwenden würde und die Dauer pro Ausführung 60s beträgt, aber dieser Vorgang im 10 Sekunden Intervall ausgeführt wird, dann müsste sich diese doch kleinweise stapeln, wenn sie hintereinander ausgeführt werden. Oder liege ich da falsch?

LG

16.842 Beiträge seit 2008
vor 7 Jahren

Nein, für sowas gibt es etablierte Pattern, zB. Pipelining.
Wenn Du also im Detail erklärst, was Du eigentlich genau erreichen willst und in welcher Umgebung Du Dich befindest, dann kann man Dich entsprechend in eine Richtung schubsen.

Der Einwurf mit dem Locking:
Im einfachsten Fall einfach irgendwo einen Bool merken, ob aktuell eine Abarbeitung läuft, und dann eben keine starten, wenn eine läuft.
Oder eben warten, bis die aktuelle zuende ist. Aber das kommt halt drauf an, was Du denn wirklich erreichen willst.

Wäre also ganz gut, wenn Du beschreibst, was Du tun willst.
Denn nur so kann man diese ganze Situation lösen, statt nur Dein aktuelles Problem zu beheben, das evtl. verbesserungsfähig ist.

5.658 Beiträge seit 2006
vor 7 Jahren

Hi Red-Sh4nks,

du solltest den nächsten Speichervorgang erst ausführen, wenn der vorherige abgeschlossen ist. Das AutoBackup nützt ja nichts, wenn du aufgrund der seriellen Abarbeitung Daten von vor 30 Minuten speicherst, während die aktuellen Daten ganz hinten in der Queue liegen und bei einem Programmabsturz verloren gehen.

Paralleles Schreiben auf Festplatten ist total sinnfrei.

Ich vermute, mit "Parallel" ist hier "im Hintergrund" bzw. "nicht blockierend" gemeint.

// Edit:
Wenn es eine Anforderung gibt, daß die Daten immer aktuell gehalten und nach einem Absturz wiederhergestellt werden können, sollte man auch mal die Verwendung einer Datenbank in Betracht ziehen. So spart man sich, immer alle Daten (das scheint ja bei dir eine große Menge zu sein, wenn es so lange dauert) zu speichern. XML- oder andere Dateien kann man ja aus der Datenbank immernoch generieren, wenn notwendig.

Weeks of programming can save you hours of planning

R
Red-Sh4nks Themenstarter:in
47 Beiträge seit 2011
vor 7 Jahren

@MrSparkle: Ja, da hast du recht!

@Abt: Nachdem sich meine Problemstellung geändert hat, dachte ich, dass es besser wäre, wenn ich mich zuerst durchs Forum wühle bevor ich euch mit Frage löchere 😃 Aber der Vollständigkeit zu liebe das neue Problem:

Es existieren 2 ineinander verschachtelte for-Schleifen mit jeweils etwa 400 Durchläufen (insgesamt also ~400^2 = 160.000 Durchläufe). Der Code sieht in etwa so aus:


string Speicher = "";

for (int i = 1; i < 390; i++)
            {
                Speicher += KlassenInstanz.getInfoXY().ToString();
                ...

                for (int j = 1; j < 400; j++)
                {
                    Speicher += KlassenInstanz.getI(i).getJ(j).ToString();
                }
            }

Ich werde jetzt mal versuchen diese inner for-Schleife zu ersetzen. Wenn das nicht klappt, wühle ich mich mich mal wieder durch das Forum.

EDIT: Hab die innere for-Schleife mit der String.Join Methode ersetzen können. Damit ist die Speicherzeit auf 8 Sekunden gesunken.

5.658 Beiträge seit 2006
vor 7 Jahren

Eventuell kannst du mit einem StringBuilder noch etwas mehr Performance herauskitzeln.

// Edit:
... oder direkt in einen FileStream schreiben, so daß man sich das Zwischenspeichern in einen String komplett sparen kann.

Weeks of programming can save you hours of planning

G
17 Beiträge seit 2015
vor 7 Jahren

Hallo Red-Sh4nks,

das Zeitintenstivste ist primär die String-Verkettung mittels "+=" Operator in deinem Code. Dabei wird jedesmal (also in deinem Fall 160000 mal) ein neuer String aus zwei anderen erzeugt.
Wenn du statt dessen einen StringBuilder verwendest und entsprechend deiner erwarteten Größe (also ca. 500000 Zeichen) im Konstruktor erzeugst, sollte dein Performance-Problem beseitigt sein.


StringBuilder Speicher = new StringBuilder(500000);

for (int i = 1; i < 390; i++)
            {
                Speicher.Append(KlassenInstanz.getInfoXY().ToString());
                ...

                for (int j = 1; j < 400; j++)
                {
                    Speicher.Append(KlassenInstanz.getI(i).getJ(j).ToString());
                }
            }
string fertigerSpeicher = Speicher.ToString();

R
Red-Sh4nks Themenstarter:in
47 Beiträge seit 2011
vor 7 Jahren

Hallo Grombi! Hallo MrSparkle!

Vielen vielen Danke für eure Hinweise. Mit Stringbuilder konnte ich die Datensammlungsdauer samt Speichervorgang von inzwischen ~8 Sekunden noch weiter runter auf 0,5 Sekunden reduzieren. Damit kann ich leben. Herzlichen Dank!

LG