Eclipse IDE und Quad core


#27

Wenn du die System.out.println() aus dem catch und aus dem Konstruktor nimmst, sieht es wieder anders aus. Bei mir (nur mit der Hälfte der Daten getestet) kommt dann folgendes:

Schreiben: 25145
Lesen:     24569
Parsen:    20903

#28

Ich habe das Programm ausgeführt und folgende Werte erhalten:

Runtime.getRuntime().totalMemory() = 109
Runtime.getRuntime().maxMemory() = 7282
Runtime.getRuntime().freeMemory() = 106

Runtime.getRuntime().totalMemory() = 4187
Runtime.getRuntime().maxMemory() = 7282
Runtime.getRuntime().freeMemory() = 193

Es gab eine NFE: For input string: "a"
Es gab eine NFE: For input string: "a"
Es gab eine NFE: For input string: "a"
Es gab eine NFE: For input string: "a"
Es gab eine NFE: For input string: "a"
Es gab eine NFE: For input string: "a"
f_drei = 0.38539296
f_drei = 0.14120781
f_drei = 0.24210173
f_drei = 0.017682254
Runtime.getRuntime().totalMemory() = 3760
Runtime.getRuntime().maxMemory() = 7282
Runtime.getRuntime().freeMemory() = 1710

Schreiben: 50761
Lesen:     40405
Parsen:    49995

Diese decken sich jetzt mit deinen Werten ±, nicht signifikant.

Bereits jetzt sieht man, dass das Lesen kein Problem ist. - Dies ist auch so, wie hier im Thread beschrieben.

Jedoch ist dies (deine Implementierung) kein Beweis, dass die CPU dafür verantwortlich ist. Die CPU wird in diesem Fall nicht mehrere Kerne nutzen und sich wohl die meiste Zeit langweilen.

Siehe auch hier: https://stackoverflow.com/questions/4436422/how-does-java-makes-use-of-multiple-cores

Mangels Multithreading impl, auch nur ein Kern. Daher nutzt du hier nicht einmal das volle Potential deiner CPU.

Selbst ein Multithreading an dieser Stelle anzubringen wäre unvorteilhaft, da bei so vielen Daten das Management der Threads ein höheren Aufwand darstellt, als die Berechnung selbst.

Außerdem könnte ich hier jede pot. komplizierte Rechenoperation durchführen und dann sagen, ja das liegt an der CPU.

Selbst mit dem CSVParser von Apache (siehe oben) konnte man die Zeiten nicht senken. Jedoch ist der benötigte Speicher signifikant geringer. Einfach nur aus dem, dass man Zeilenweise liest und nicht die Datei im Speicher behält. Dies wäre auch an dieser Stelle eine definitive Empfehlung eine CSV Datei nicht erst komplett zu lesen und dann zu verarbeiten. Bei CSV macht dies keinen Sinn.

Wenn wir jedoch von diesem erdachten Szenario wieder auf die 100 MB des TO gehen, sollte dies alles keine Signifikanz haben.

Die Zeit, die verloren geht, kennen wir nicht und können nichts weiter analysieren. Jetzt können wir aber definitiv sagen, dass es mit dem Lesen und Parsen der Datei nichts zu tun hat.


#29

Wenn man das auf 3 Millionen Zeilen runterbricht (um ca. 100MB zu haben), sind es bei mir hier auf einer lahmen, alten Kiste mit HDD diese Zahlen:

Schreiben: 6203
Lesen: 2204
Parsen: 6281

Aber die Aussagekraft dieses Benchmarks ist recht gering. Ein Detail ist, dass dieselbe Datei geschrieben und sofort gelesen wird. Das allein kann das Ergebnis verzerren. Darüber hinaus kann man die konzeptuelle Frage stellen, WAS nun WO gemacht werden soll. Es gibt ja verschiedene Granularitäten

byte data[] = readBytes("file.csv");
process(data);

oder

List<String> lines = readLines("file.csv");
process(lines);

oder

List<CSVRecord> records = readRecords("file.csv"); // Grob wie Apache CSV
process(records);

oder oder oder.


Schon das reine Lesen, auf der Ebene eines byte[]-Arrays bietet einige Freiheitsgrade. Die im Test verwendete AlsBlobLaden-Methode liest jedes einzelne Byte und schreibt es wieder zurück. Das ist

richtig schlecht.

Zum Glück wurden die Freiheitsgrade, die es da gibt, mit den neuen Utility-Methoden etwas eingedampft, so das es leichter ist, das richtig zu machen. Im speziellen gibt es ja Files.readAllBytes, was intern NIO mit FileChannel & Co verwendet. Schon diese AlsBlobLaden-Methode zu umgehen, und die entsprechende Zeile zu ändern in

ByteArrayInputStream BuArrayOutputStreamToBuArrayInputStream = 
    new ByteArrayInputStream(Files.readAllBytes(Paths.get("csv.txt")));

führt bei mir (nochmal: auf einer alten, langsamen HDD) zu diesen Zahlen:

Schreiben: 6469
Lesen: 343
Parsen: 6266

D.h. mit einer winzigen Änderung wird das ganze auf einmal sechs mal so schnell.

Und auch nochmal: Die Aussagekraft des Tests ist immernoch gering, aber zumindest sollte das schon einige Dinge zeigen:

  • Man sollte wissen, was man messen will
  • Man sollte messen, und zwar richtig
  • Man sollte die richtigen Schlüsse aus den Messergebnissen ziehen
  • Schnellere Hardware ist kein Ersatz für ““gutes”” Programmieren

#30

Ohne diese 11 bis 12 Ausgaben, optimiert er vielleicht zu stark. (Dann sind es Variablen/Objekte, gar nicht genutzt.)

Ich glaub es waren 200. Mit so wenigen MB gibt es ein paar “unerwünschte Effekte”.

Vor Java 8/9 musste man sich auf die byte[]-Ebene begeben. (Edit: Es wird auch nicht jedes Byte einzeln… es ist ja ein Buffer dabei.)

Ja, und wie vermeidest du parseFloat, try-catch, neues Objekt, split, usw. usf. - kurz: Das String handling, das ich schon oben angesprochen hatte (war’s im ersten Beitrag von mir?).

Das hab ich auch nicht behauptet. :blush:


#31

Hab mir jetzt den i7 gekauft, Wildfly startet in wenigen Sekunden, die Datei liest auch wesentlich schneller ein, ca 1 Minute :slight_smile:


#32

Und wenn du mehr Code zeigst, wie du sie einliest, drücken wir das auch noch auf 30 Sekunden :sunglasses:


#33

Letztens auf Hackernews gelesen: Quick Tips for Fast Code und was ich besonders schön fand ist einer der Kompentare dazu, der alles so kompakt auf den Punkt bringt.

https://news.ycombinator.com/item?id=16040359

  1. Messen und das ganze auch in entsprechenden Tests verfolgen (Regression Test)
  2. Mit einem Do-Nothing-Loop anfangen, der einmal nur über den Input geht und nichts tut. Das ist die Base-Line, besser wirds nicht. [Anmerkung: Wenn man das einlesen und das verarbeiten noch voneinander trennen kann im Sinne von OO, dann kann man, sollte man etwas besseres zum Einlesen finden dies entsprechend austauschen, für jedes seiner Programme]
  3. Schrittweise den Algorithmus aufbauen und schauen, an welchem Punkt die Performance einbricht. An den Stellen lohnt es sich dann zu überlegen, was man besser machen kann.

Auch wenn das ganze jetzt sehr sehr oberflächlich daherkommt, man könnte tausende Fälle bringen, bei denen jemand meinte er könne da was besser machen…


Zu der Frage welche Prozessoren zur Auswahl standen, habe ich mal die Wikipedia und Passmark bemüht.
Bei den Quadcore i7, stehen der 7700HQ (8899 Punkte) und 7820HQ (9421) zur Auswahl.
Mit i5 zum Beispiel 7360U (6080) und 7267U (5350) wenn es denn die 17. Generation sein soll. So gesehen sind es 50 bis 80% mehr an Leistung die man von einem i7 erwarten kann. Also auch bei einem i5 würde ich mit unter 2 Minuten rechnen.

Und wenn ich es richtig herauslese, dann handelt es sich bei dem 2012er i5 um einen 3210M, der ungefähr 3813 Punkte schafft. Da ist die halbe Stunde auch weit entfernt, 3 Minuten wären OK, aber eine halbe Stunde fällt da schon aus dem Rahmen. Die größten Flaschenhälse bei den 2012er MacBooks sind die Festplatten, sofern noch keine SSD drin ist. Bei der Aufpreispolitik von Apple ist die Chance aber dennoch sehr gross, das da was mechanisches drin steckt. Arbeitsspeicher ist mit 4 bzw. 8 GB in der Regel ausreichend vorhanden.