Alles eine Frage der Optimierung

So stand ich vor der Aufgabe eine Datei mit zig Zeichenfolgen zu überarbeiten. Also prinzipiell Duplikate löschen und die Datei sortieren.

Viele Wege führen nach Rom. Die Eingangsdatei hatte knapp 19 Millionen Zeilen - also Zeichenfolgen. Rund 190MB. Man könnte es schon Stress-Test nennen.

Wie setzt man dieses jetzt um? Also erst mal die Datei einlesen. Das geht recht unproblematisch mittels System.IO.File.ReadAllLines. Somit hat man jede Zeichenfolge als ein Element in einem String Array. Aber wie geht es weiter?

1. Versuch

Eine For Each Schleife, jedes Element in eine System.Collections.Generic.List(Of String) kopieren, vorher überprüfen lassen ob es nicht bereits existiert. Ich sage mal so... Ja, es geht. Nach 2 Wochen nonstop Laufzeit war er aber erst bei der Hälfte der Daten angelangt und wurde immer langsamer je weiter es fortgeschritten war. Unbrauchbar.

2. Versuch

Wieder For Each, dieses Mal mit einem Array gleicher Größe als Ziel. Abfrage per Array.IndexOf damit nichts doppelt rein kommt. Vergiss es. Das hätte nicht nur Monate sondern Jahre gedauert.

3. Versuch

Das Selbe wie in Versuch 2, nur mit Array.BinarySearch. Tjo, is schnell. Löschte auch hier und da etwas raus, Duplikate waren aber immer noch drin. Auch wieder unbrauchbar.

4. Versuch

Das Array vorher sortiert per Array.Sort, sonst wie im 3. Versuch. Selbes Ergebnis.

Jetzt musste der Gockel herhalten. Viele mit ähnlichen Problemen, aber ohne brauchbare Lösungen. Aber ein Gedankenansatz hat weiter geholfen. Der Code funktionierte natürlich nicht, aber die Grundidee war richtig.

5. Versuch

Also, Ausgangssituation ist immer noch ein Array mit den Zeichenfolgen aus der Datei. Dann das Array sortieren lassen (Array.Sort). Dann eine For Next Schleife erstellen die rückwärts durch das Array geht.

For i As Integer = Content.Length - 1 To 1 Step -1
	If String.Equals(Content(i), Content(i - 1)) Then Content(i) = String.Empty
Next

Es wird der Wert an der aktuellen Position mit der vorigen verglichen. Wenn identisch wird der aktuelle Wert gelöscht / auf String.Empty gesetzt. Später wird ein neues Array erstellt und alle Werte die nicht leer sind rein kopiert. Laufzeit: ca. 2 Minuten, da war's schon fertig.

Dieses Grundprinzip kann man garantiert nicht nur mit Strings verwenden. Auch könnte man natürlich noch ein Dictionary erstellen lassen welche Einträge wie oft vorgekommen sind, eine Variable wie viele Einträge insgesamt entfernt wurden, ...

Warum ich das jetzt hier schreibe?

Ganz einfach. Erstens um Anderen zu helfen, aber auch um ganz klar aufzuzeigen dass viele Wege nach Rom führen. Die Erde ist ja 'ne Kugel, irgendwann wird man schon ankommen. Das Sprichwort ist hier wörtlich zu nehmen. Unzählige Entwickler programmieren, haben wohl auch gute Ideen, aber funktioniert es erst mal dann... tja... funktioniert ja. Auf Performance wird heutzutage gar nicht mehr geachtet.

Leute, achtet mal wieder auf Performance und klatscht nicht blind irgendwas zusammen! Und damit meine ich bei Weitem nicht nur Hobby-Programmierer sondern auch große, namenhafte Firmen! Klar, Optimierung kostet Zeit und somit auch Geld, aber die Benutzer wird es freuen.

Ich schließe mal mit dem Satz ab:

"Qualität ist, wenn der Kunde zurück kommt, nicht das Produkt!"

Edit: Ach ja, das Projekt hat sich gelohnt. Von 187 MB runter auf 145 MB ;)