Nebenläufigkeit: Java Map

9

Was ist der beste Weg, um 20 Millionen Objekte in ein Java-Map-Objekt zu schieben?

  1. Ohne Multi-Threading dauert es ~ 40 Sekunden.
  2. Mit ForkJoinPool dauert es ~ 25 Sekunden, wo ich 2 Aufgaben erstellt habe und jede dieser Aufgaben 10 Millionen Entitäten
  3. vorantreibt

Ich glaube, dass beide Aufgaben in zwei verschiedenen Kernen laufen. Frage: Wenn ich eine Aufgabe erstelle, die 10 Millionen Daten schiebt, dauert es ~ 9 Sekunden. Wenn dann 2 Aufgaben ausgeführt werden, bei denen jede dieser Aufgaben 10 Millionen Daten schiebt, warum dauert es ~ 26 Sekunden? Mache ich etwas falsch?

Gibt es eine andere Lösung zum Einfügen von 20-M-Daten, bei denen es weniger als 10 Sekunden dauert?

    
PhiberOptixz 28.07.2017, 12:00
quelle

2 Antworten

0

Ohne den Code zu sehen, ist die wahrscheinlichste Ursache für diese schlechten Leistungsergebnisse auf die Speicherbereinigung zurückzuführen. Um es zu demonstrieren, habe ich folgendes Programm geschrieben:

%Vor%

Am Ende der Verarbeitung berechnet dieser Code eine Annäherung an den verwendeten Speicher, indem eine System.gc() unmittelbar gefolgt von Runtime.maxMemory() - Runtime.freeMemory() ausgeführt wird. Dies zeigt, dass die Karte mit 20 Millionen Einträgen ungefähr knapp 2,2 GB groß ist, was beträchtlich ist. Ich habe es mit 1 und 2 Threads ausgeführt, für verschiedene Werte der -Xmx und -Xms JVM-Argumente, hier sind die resultierenden Ausgaben (nur um klar zu sein: 2560m = 2.5g):

%Vor%

Diese Ergebnisse können in der folgenden Tabelle zusammengefasst werden:

%Vor%

Ich beobachtete auch, dass es für die Heapgrößen 2,5 g und 3 g eine hohe CPU-Aktivität mit Spikes bei 100% während der gesamten Verarbeitungszeit aufgrund der GC-Aktivität gab, während für 4 g und 8 g nur beobachtet wurde am Ende aufgrund des Aufrufs System.gc() .

Zum Schluss:

  1. Wenn Ihr Heap unangemessen skaliert ist, werden durch die Garbage Collection alle Leistungssteigerungen zunichte gemacht, die Sie erwarten würden. Sie sollten es groß genug machen, um die Nebenwirkungen langer GC-Pausen zu vermeiden.

  2. Sie müssen sich auch darüber im Klaren sein, dass die Verwendung einer gleichzeitigen Sammlung wie ConcurrentHashMap einen erheblichen Leistungsaufwand verursacht. Um dies zu verdeutlichen, habe ich den Code leicht modifiziert, so dass jede Aufgabe ihre eigene HashMap verwendet, und am Ende werden alle Karten in der Karte der ersten Aufgabe aggregiert (mit Map.putAll() ). Die Bearbeitungszeit fiel auf ca. 3200 ms

Lolo 29.07.2017 08:57
quelle
0

Eine Addition dauert wahrscheinlich einen CPU-Zyklus. Wenn Ihre CPU also bei 3 GHz läuft, sind es 0,3 Nanosekunden. Mach es 20M mal und das wird 6000000 Nanosekunden oder 6 Millisekunden. Ihre Messung wird also mehr durch den Overhead von Start-Threads, Thread-Switching, JIT-Compilation usw. beeinflusst als durch die Operation, die Sie sind versuchen zu messen.

Die Garbage-Collection kann ebenfalls eine Rolle spielen, da sie Sie möglicherweise verlangsamen kann.

Ich schlage vor, dass Sie eine spezialisierte Bibliothek für Micro-Benchmarking verwenden, wie zum Beispiel jmh.

Danke an assylias Beitrag, der mir geholfen hat, meine Antwort zu schreiben

    
John D 29.07.2017 09:09
quelle