Was ist der beste Weg, um 20 Millionen Objekte in ein Java-Map-Objekt zu schieben?
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?
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):
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:
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.
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
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
Tags und Links java multithreading java.util.concurrent