Entschuldigt zuerst, das ich mein Thema so in „Allgemeine Themen“ knalle. Ich wusste nicht wohin damit
Aber zum Problem:
Ich habe eine 2GB große CSV Datei, was in meinem Fall so 30Mio ungeordnete Datensätze sind.
Ich würde die Datensätze gerne nach Datum (Bsp. 20150403) und Uhrzeit (Bsp. 023104) ordnen
Bisher habe ich sie in eingelesen und in einer ArrayList abgelegt → Ging so ne halbe Stunde
Dann hab ich die Liste sortiert → Ging ruck zuck
Aber das schreiben würde nun hochgerechnet ca. 5 Tage dauern
Hat jemand vielleicht irgendeine Idee wie ich das schneller schreiben kann oder auch gerne einen anderen Ansatz zur Lösung
Vielen Danke schon im voraus
Etwas Code wäre an dieser Stelle sicherlich angebracht, eventuell liegt es ja nur an einer Kleinigkeit, die dem ein oder anderen sofort ins Auge springt.
Ganz ehrlich, geh über eine Datenbank - H2 oder so reicht aus. Du kannst dann sequentiell hineinschreiben und lesen, und auch ganz leicht sortieren, wie du willst. CSV hat so einige Tücken (wenn du keine Kontrolle über die Erzeugung hast), da würde ich OpenCSV empfehlen.
das Sortieren ist ja offensichtlich die geringste Schwierigkeit,
falls Einlesen und Schreiben selber zu machen wäre (“Du kannst dann sequentiell hineinschreiben und lesen”), wäre nichts gewonnen
und auch noch in der Liste der Mutmach-Posts: 2 GB zu lesen oder zu schreiben sollte jeweils nicht mehr als 1 Min. dauern, muss nur richtig gemacht werden
Ich bin mir nicht sicher aber eventuell könnte es ein wenig helfen das bw.flush() direkt vor das close zu machen.
Könnte mir jedenfalls vorstellen, dass das etwsa bringen könnte
Das Mantra für bessere Performanz: “Am schnellesten ist das, was man nicht tut…”
Das trifft ganz besonders auf schleifen zu.
Also:
Das Überspringen der ersten Zeile machst Du ab besten, in dem Du vor der Schleife ein mal FileReader.readLine() aufrufts, dann fällt das if in der Schleife weg.
Außerdem kannst Du Zeile 10 auch vor die Schleife ziehen. Lieber ein mal ein Objekt um sonst erzeugen, als Millionen mal.
Das sollte das Lesen deutlich beschleunigen.
Zum Schreiben fällt mich nichts ein.
[quote=Clayn]Ich bin mir nicht sicher aber eventuell könnte es ein wenig helfen dasbw.flush()direkt vor das close zu machen.[/quote]Du hast recht, dass ist die Schreibbremse.
[QUOTE=Clayn]Ich bin mir nicht sicher aber eventuell könnte es ein wenig helfen das bw.flush() direkt vor das close zu machen.
Könnte mir jedenfalls vorstellen, dass das etwsa bringen könnte[/QUOTE]
Hab ich schon probiert, aber macht es leider auch nicht ersichtlich schneller
immer etwas Vorsicht bei Erzeugung von Ressourcen wie SimpleDateFormat in Schleifen,
muss nicht millionenfach angelegt werden, 1x reicht,
flush() besser auch aus der Schleife raus, ja,
ein BufferedWriter soll ja gerade erst mehrere Schreibkommandos sammeln, nur bei größeren gesammelten Mengen selten schreiben
wie sieht es eigentlich bei kleineren Dateigrößen aus, bei 1 Mio. Datensätze Schreiben 5 Tage/ 30 ~4 Stunden?
zum Vergleich auf die Schnelle ein (vollständiges!) Testprogramm, welches 6 Mio. einfache Einträge generiert und in 300 MB-Datei schreibt, in 17 sec bei mir,
davon das Schreiben nur etwa 1 sec, bin selber überrascht, meine alte Daumenregel ist 100 MB/sec für Lesen/ Schreiben/ Kopieren
SimpleDateFormat in die Generierungsschleife macht es bei mir deutlich langsamer, 3fache Zeit zum Generieren,
flush in die Schleife ~verfünfzehnfacht das Schreiben in meinem Beispiel
die Daten zu 300 MB CSV reichen schon, bei mir 1 GB an Arbeitsspeicher zu füllen, und mehr kann ich gerade nicht zusammenkonfigurieren…
public class Test2
{
public static void main(String[] args)
throws Exception
{
SimpleDateFormat sdfSource = new SimpleDateFormat("yyyyMMdd HHmmss");
List<Datensatz> datensatzList = new ArrayList<>();
final int n = 6000000;
final int m = n/10;
long j = 0;
long start = System.currentTimeMillis();
for (int i = 0; i < n; i++)
{
Date date = new Date();
String line = sdfSource.format(date) + ";halli hallo;mittellange Zeile;" + i;
datensatzList.add(new Datensatz(line));
if (j++ > m)
{
System.out.println("generiert: " + (System.currentTimeMillis() - start) + " - 10%");
j = 0;
}
}
System.out.println("fertig generiert: " + (System.currentTimeMillis() - start));
System.out.println("Schreiben");
File f = new File("alldata_new.csv");
BufferedWriter bw = new BufferedWriter(new FileWriter(f));
j = 0;
for (Datensatz ds : datensatzList)
{
bw.write(ds.getLine());
bw.write("
");
if (j++ > m)
{
System.out.println("schreiben: " + (System.currentTimeMillis() - start) + " - 10%");
j = 0;
}
}
bw.flush();
bw.close();
System.out.println("fertig schreiben: " + (System.currentTimeMillis() - start));
System.out.println("Dateigröße: " + f.length());
}
}
class Datensatz
{
String line;
public Datensatz(String line)
{
this.line = line;
}
public String getLine()
{
return this.line;
}
}
Zeile 5 mit System.out.println(zeile); ist auch ganz böse.
Beim Einlesen jede Zeile auf System.out auszugeben ist sehr sehr teuer.
Das SimpleDateFormat einmalig zu initialisieren wurde ja bereits genannt.
Dennoch ist das Parsen und umwandeln in ein Date-Objekt auch mit einigem Aufwand verbunden.
Dazu kommt noch das du nun eine neue Datenstruktur Datensatz benötigst, die ebenfalls Speicher beansprucht.
Den einfachste Weg, da Datum und Uhrzeit ja schon strukturiert und als String sortierbar sind, diese an den Anfang einer Zeile zu hängen.
liste.add(split[2] + split[3] + zeile);
Danach sortieren als ganz normalen String.
liste.sort(String.CASE_INSENSITIVE_ORDER);
Zum Ausgeben der Daten würde ich einen PrintWriter verwenden und dabei die Anfangs hinzugefügten Zeitwerte entfernen.
out.println(item.substring(14));
Zusammengefasst so:
try(FileReader fileReader = new FileReader(inputFilename)) {
fileReader.readLine(); //Erste Zeile lesen
while (null != (zeile = fileReader.readLine())) {
String[] split = zeile.split(";");
list.add(split[2] + split[3] + zeile);
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}
System.out.println("Sortieren");
Collections.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println("Schreiben");
try(PrintWriter out = new PrintWriter(outputFilename)) {
for (String item : list) {
out.println(item.substring(14));
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}```
Und das Programm mit ordentlich Heap laufen lassen;)