Ich erstelle einen unendlichen Strom von Ganzzahlen, der bei 200 Millionen beginnt, filtere diesen Strom mit einer naiven Primalitäts-Test-Implementierung, um eine Last zu erzeugen und das Ergebnis auf 10 zu begrenzen.
%Vor%Dies funktioniert wie erwartet.
Wenn ich jetzt vor dem Filtern einen Aufruf von parallel () anfüge, wird nichts erzeugt und die Verarbeitung wird nicht abgeschlossen.
%Vor%Kann mir jemand in die richtige Richtung zeigen, was hier passiert?
EDIT: Ich bin nicht auf der Suche nach besseren Primality-Test-Implementierungen (es soll eine lang laufende Implementierung sein), sondern nach einer Erklärung der negativen Auswirkungen der Verwendung eines parallelen Stroms.
Die Verarbeitung wird tatsächlich abgeschlossen. Dies kann je nach Anzahl der Hardware-Threads auf Ihrem Computer jedoch ziemlich lange dauern. API-Dokumentation zu Limit warnt davor möglicherweise für parallele Streams langsam.
Tatsächlich teilt der parallele Strom zuerst die Berechnung auf die verschiedenen Teile entsprechend der verfügbaren Parallelitätsstufe auf, führt eine Berechnung für jeden Teil durch und verbindet dann die Ergebnisse zusammen. Wie viele Teile hast du in deiner Aufgabe? Eine pro gemeinsamer FJP-Thread (= Runtime.getRuntime().availableProcessors()
) plus (manchmal?) Eine für den aktuellen Thread, wenn es nicht in FJP ist. Sie können es steuern, indem Sie
Praktisch für Ihre Aufgabe ist die niedrigere Zahl, die Sie einstellen, desto schneller wird sie berechnet.
Wie teilt man die unbegrenzte Aufgabe auf? Ihre bestimmte Aufgabe wird von IteratorSpliterator bearbeitet, die trySplit Methode erstellt Chunks von immer größer werdender Größe ab 1024. Sie können selbst versuchen:
%Vor%So behandelt der erste Brocken Zahlen des Bereichs 200000000-200001023, der zweite behandelt Zahlen des Bereichs 200001024-200003071 und so weiter. Wenn Sie nur einen Hardware-Thread haben, wird Ihre Aufgabe in zwei Teile aufgeteilt, so dass 3072 geprüft wird. Wenn Sie 8 Hardware-Threads haben, wird Ihre Aufgabe in 9 Chunks geteilt und 46080 Nummern werden überprüft. Erst nachdem alle Chunks verarbeitet sind, hört die parallele Berechnung auf. Die Heuristik des Aufteilens der Aufgabe auf so große Brocken funktioniert in Ihrem Fall nicht gut, aber Sie würden den Leistungsschub sehen, wenn die Primzahlen um diese Region einmal in mehreren tausend Zahlen erscheinen würden.
Wahrscheinlich könnte Ihr spezielles Szenario intern optimiert werden (d. h. die Berechnung stoppen, wenn der erste Thread diese Grenzbedingung bereits gefunden hat). Fühlen Sie sich frei, einen Bug an den Java Bug Tracker zu melden.
Aktualisieren nachdem ich mehr in der Stream-API gegraben habe, kam ich zu dem Schluss, dass das aktuelle Verhalten ein Fehler ist, hat ein Problem gemeldet und einen Patch gepostet. Es ist wahrscheinlich, dass der Patch für JDK9 akzeptiert wird und wahrscheinlich sogar in den JDK 8u-Zweig zurückportiert wird. Mit meinem Patch verbessert die parallele Version immer noch nicht die Performance, aber zumindest ist ihre Arbeitszeit vergleichbar mit der sequentiellen Stream-Arbeitszeit.
Der Grund dafür, dass parallel
stream so lange dauert, ist die Tatsache, dass alle parallelen Streams common fork-join thread pool
verwenden und Sie eine lang andauernde Aufgabe senden (weil Ihre Implementierung von isPrime
method nicht effizient ist) Blockieren aller Threads im Pool, wodurch alle anderen Tasks, die den parallelen Stream verwenden, blockiert sind.
Um die parallele Version schneller zu machen, können Sie isPrime effizienter. Zum Beispiel
%Vor%Und sofort werden Sie die Verbesserung der Leistung bemerken. Vermeiden Sie im Allgemeinen die Verwendung paralleler Streams, wenn die Möglichkeit besteht, Threads im Pool zu blockieren.
Tags und Links java-8 concurrency java-stream