Mit Java 8 und Lambdas ist es einfach, Sammlungen als Streams zu durchlaufen und einen parallelen Stream genauso einfach zu verwenden. Zwei Beispiele aus den Dokumenten , das zweite mit parallelStream:
myShapesCollection.stream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
myShapesCollection.parallelStream() // <-- This one uses parallel
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
Wäre es immer vorteilhaft, die Parallele zu verwenden, solange mir die Bestellung egal ist? Man würde denken, es ist schneller, die Arbeit auf mehr Kerne aufzuteilen.
Gibt es noch andere Überlegungen? Wann sollte ein paralleler Stream verwendet werden und wann sollte der nicht parallele Stream verwendet werden?
(Diese Frage wird gestellt, um eine Diskussion darüber auszulösen, wie und wann parallele Streams verwendet werden sollen, nicht weil ich es für eine gute Idee halte, sie immer zu verwenden.)
quelle
Runnable
die ich aufrufestart()
, um sie alsThreads
zu verwenden. Ist es in Ordnung, dies in die Verwendung von Java 8-Streams in einer.forEach()
Parallelisierung zu ändern ? Dann könnte ich den Thread-Code aus der Klasse entfernen. Aber gibt es irgendwelche Nachteile?Die Stream-API wurde entwickelt, um das Schreiben von Berechnungen auf eine Weise zu vereinfachen, die von der Art und Weise, wie sie ausgeführt werden, abstrahiert ist, und um das Umschalten zwischen sequentiell und parallel zu vereinfachen.
Nur weil es einfach ist, heißt das nicht, dass es immer eine gute Idee ist, und in der Tat ist es eine schlechte Idee, einfach
.parallel()
überall vorbeizuschauen, nur weil man es kann.Beachten Sie zunächst, dass Parallelität keine anderen Vorteile bietet als die Möglichkeit einer schnelleren Ausführung, wenn mehr Kerne verfügbar sind. Eine parallele Ausführung erfordert immer mehr Arbeit als eine sequentielle, da neben der Lösung des Problems auch das Versenden und Koordinieren von Unteraufgaben durchgeführt werden muss. Die Hoffnung ist, dass Sie schneller zur Antwort gelangen, indem Sie die Arbeit auf mehrere Prozessoren aufteilen. Ob dies tatsächlich geschieht, hängt von vielen Faktoren ab, einschließlich der Größe Ihres Datensatzes, der Anzahl der Berechnungen, die Sie für jedes Element ausführen, der Art der Berechnung (insbesondere interagiert die Verarbeitung eines Elements mit der Verarbeitung anderer Elemente?). , die Anzahl der verfügbaren Prozessoren und die Anzahl der anderen Aufgaben, die um diese Prozessoren konkurrieren.
Beachten Sie außerdem, dass Parallelität häufig auch Nichtdeterminismus in der Berechnung aufdeckt, der häufig durch sequentielle Implementierungen verborgen wird. Manchmal spielt dies keine Rolle oder kann durch Einschränkung der beteiligten Vorgänge gemildert werden (dh Reduktionsoperatoren müssen zustandslos und assoziativ sein.)
In der Realität beschleunigt Parallelität manchmal Ihre Berechnung, manchmal nicht und manchmal sogar. Es ist am besten, zuerst mit sequentieller Ausführung zu entwickeln und dann wo Parallelität anzuwenden
(A) Sie wissen, dass eine Leistungssteigerung tatsächlich Vorteile bringt und
(B) dass es tatsächlich eine erhöhte Leistung liefert.
(A) ist ein geschäftliches Problem, kein technisches. Wenn Sie ein Leistungsexperte sind, können Sie normalerweise den Code betrachten und (B) bestimmen, aber der kluge Weg ist zu messen. (Und stören Sie sich nicht einmal, bis Sie von (A) überzeugt sind. Wenn der Code schnell genug ist, sollten Sie Ihre Gehirnzyklen besser an anderer Stelle anwenden.)
Das einfachste Leistungsmodell für Parallelität ist das "NQ" -Modell, wobei N die Anzahl der Elemente und Q die Berechnung pro Element ist. Im Allgemeinen muss der Produkt-NQ einen bestimmten Schwellenwert überschreiten, bevor Sie einen Leistungsvorteil erzielen. Bei einem Problem mit niedrigem Q wie "Addiere Zahlen von 1 bis N" wird im Allgemeinen eine Gewinnschwelle zwischen N = 1000 und N = 10000 angezeigt. Bei Problemen mit höherem Q sehen Sie Breakevens bei niedrigeren Schwellenwerten.
Aber die Realität ist ziemlich kompliziert. Identifizieren Sie also erst, wenn die sequentielle Verarbeitung Sie tatsächlich etwas kostet, und messen Sie dann, ob Parallelität hilfreich ist, bis Sie Erfahrung gesammelt haben.
quelle
findAny
anstelle vonfindFirst
...myListOfURLs.stream().map((url) -> downloadPage(url))...
. B. ).ForkJoinPool.commonPool()
und Sie möchten nicht, dass blockierende Aufgaben dorthin gehen.Ich habe mir eine der Präsentationen von Brian Goetz (Java Language Architect & Spezifikationsleiter für Lambda Expressions) angesehen . Er erklärt ausführlich die folgenden 4 Punkte, die vor der Parallelisierung zu beachten sind:
Aufteilungs- / Zerlegungskosten
- Manchmal ist das Aufteilen teurer als nur die Arbeit zu erledigen!
Kosten für Aufgabenversand / -verwaltung
- Kann in der Zeit, die für die Übergabe der Arbeit an einen anderen Thread erforderlich ist, viel Arbeit leisten.
Ergebniskombinationskosten
- Manchmal umfasst die Kombination das Kopieren vieler Daten. Das Hinzufügen von Zahlen ist beispielsweise billig, während das Zusammenführen von Sätzen teuer ist.
Lokalität
- Der Elefant im Raum. Dies ist ein wichtiger Punkt, den jeder übersehen kann. Sie sollten Cache-Fehler berücksichtigen. Wenn eine CPU aufgrund von Cache-Fehlern auf Daten wartet, würden Sie durch Parallelisierung nichts gewinnen. Aus diesem Grund parallelisieren Array-basierte Quellen am besten, wenn die nächsten Indizes (in der Nähe des aktuellen Index) zwischengespeichert werden und die Wahrscheinlichkeit geringer ist, dass die CPU einen Cache-Fehler erleidet.
Er erwähnt auch eine relativ einfache Formel, um die Wahrscheinlichkeit einer parallelen Beschleunigung zu bestimmen.
NQ-Modell :
Dabei ist
N = Anzahl der Datenelemente.
Q = Arbeitsaufwand pro Element
quelle
JB traf den Nagel auf den Kopf. Das einzige, was ich hinzufügen kann, ist, dass Java 8 keine reine Parallelverarbeitung durchführt, sondern paraquential . Ja, ich habe den Artikel geschrieben und mache seit 30 Jahren F / J, damit ich das Problem verstehe.
quelle
ArrayList
HashMap
Andere Antworten betrafen bereits die Profilerstellung, um vorzeitige Optimierung und Gemeinkosten bei der Parallelverarbeitung zu vermeiden. Diese Antwort erklärt die ideale Auswahl von Datenstrukturen für paralleles Streaming.
Quelle: Item # 48 Vorsicht beim parallelen Erstellen von Streams, effektives Java 3e von Joshua Bloch
quelle
Parallelisieren Sie niemals einen unendlichen Strom mit einem Limit. Folgendes passiert:
Ergebnis
Gleiches, wenn Sie verwenden
.limit(...)
Erläuterung hier: Java 8, das .parallel in einem Stream verwendet, verursacht einen OOM-Fehler
Verwenden Sie in ähnlicher Weise nicht parallel, wenn der Stream geordnet ist und viel mehr Elemente enthält, als Sie verarbeiten möchten, z
Dies kann viel länger dauern, da die parallelen Threads möglicherweise in vielen Nummernbereichen anstelle des entscheidenden 0-100 arbeiten, was sehr lange dauert.
quelle