Mit der Entwicklung von Java ist die parallele Programmierung einfacher zu schreiben, aber es ist immer noch schwierig, eine korrekte und schnelle Implementierung der parallelen Verarbeitung zu schreiben. Dies ist keine Ausnahme von der Verarbeitung von Streams in parallelen Streams. Schauen wir uns das Beispiel von Item45 an.
package tryAny.effectiveJava;
import static java.math.BigInteger.*;
import java.math.BigInteger;
import java.util.stream.Stream;
public class MersennePrimes {
public static void main(String[] args) {
primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE)).filter(mersenne -> mersenne.isProbablePrime(50))
.limit(20)
// .forEach(System.out::println);
.forEach(mp -> System.out.println(mp.bitLength() + ":" + mp));
}
static Stream<BigInteger> primes() {
return Stream.iterate(TWO, BigInteger::nextProbablePrime);
}
}
Selbst wenn parallel () in der Stream-Verarbeitung dieses Programms enthalten ist, wird die Verarbeitung nicht abgeschlossen und die CPU bleibt weiterhin hoch, anstatt die Verarbeitung zu beschleunigen.
Was passiert hier? Dies liegt einfach daran, dass die Stream-Bibliothek nicht wusste, wie diese Pipeline parallelisiert werden kann, und die heuristische Lösung fehlgeschlagen ist. ** Wenn es ursprünglich von Stream.iterate stammt oder Limit-Zwischenoperationen ausgeführt werden, ist es schwierig, die Leistung durch Parallelisierung der Pipeline zu verbessern. ** Das obige Programm enthält beide Elemente. Die Lehre hier ist, dass ** parallele Streams nicht wahllos verwendet werden **.
Im Allgemeinen kann die ** Parallelisierung die Leistung von Streams mit ArrayList, HashMap, HashSet, ConcurrentHashMap, Arrays, Int-Bereichen und langen Bereichen verbessern. ** ** ** Gemeinsam ist ihnen, dass sie leicht in Unterbereiche unterteilt werden können. Eine weitere wichtige Gemeinsamkeit dieser Datenstrukturen ist die ** Referenzstelle bei sequentieller Verarbeitung. % 85% A7% E3% 81% AE% E5% B1% 80% E6% 89% 80% E6% 80% A7) **.
Kündigungsvorgänge wirken sich auch auf die Leistung der Parallelverarbeitung aus. Wenn eine große Menge an Verarbeitung durch Terminierungsverarbeitung ausgeführt wird und die Terminierungsverarbeitung intern eine sequentielle Verarbeitung im Vergleich zur gesamten Pipeline durchführt, ist die Parallelisierung der Pipeline nicht sehr effektiv. Der effektivste Beendigungsprozess ist der Reduktionsprozess wie min, max, count und sum. Auch [Kurzschlussauswertung] wie anyMatch, allMatch, noneMatch (https://ja.wikipedia.org/wiki/%E7%9F%AD%E7%B5%A1%E8%A9%95%E4%BE%A1) Ist leicht den Effekt der Parallelisierung zu erhalten. Variable Reduktionsoperationen, die mit der Erfassungsmethode des Streams ausgeführt werden, profitieren weniger wahrscheinlich von der Parallelisierung. Dies liegt daran, dass der Verarbeitungsaufwand für das Verbinden von Sammlungen kostspielig ist.
Safety failure ** Die Stream-Parallelisierung verursacht nicht nur Leistungsprobleme, einschließlich eines Lebendigkeitsfehlers, sondern kann auch zu falschen Ergebnissen und unerwartetem Verhalten führen. (Sicherheitsfehler) ** Ein Sicherheitsfehler tritt auf, wenn Sie eine Funktion verwenden, die nicht den strengen Spezifikationen des Streams entspricht. Die zu akkumulierende Funktion und die zu kombinierende Funktion mit der an den Stream übergebenen Funktion lautet beispielsweise kombiniert. In package-summary.html # Associativity), in Non-Interference , Statuslos muss eine Funktion sein. Wenn Sie dies nicht befolgen, verursachen gerade Pipelines keine Probleme, aber parallelisierte Pipelines können katastrophale Folgen haben.
Selbst wenn die Parallelverarbeitung unter sehr guten Bedingungen durchgeführt werden kann, ist sie bedeutungslos, es sei denn, sie zeigt eine Leistung, die die Kosten der Parallelisierung ausgleicht. Eine grobe Schätzung sollte (Anzahl der Elemente im Stream) * (Anzahl der pro Element ausgeführten Codezeilen)> 100000 (wie 10000 bei Betrachtung der Linkquelle) erfüllen. (Http: //gee.cs. oswego.edu / dl / html / StreamParallelGuidance.html)).
Es sollte anerkannt werden, dass die Stream-Parallelisierung eine Leistungsoptimierung ist. Sie müssen sicherstellen, dass jede Optimierung vor und nach der Änderung getestet werden sollte. Im Idealfall sollten Tests in einer Produktionsumgebung durchgeführt werden.
** Wenn die Parallelisierung unter den richtigen Umständen durchgeführt wird, kann eine Leistungsverbesserung proportional zur Anzahl der Prozessoren erwartet werden. ** ** ** Es ist einfach, diese Leistungen in den Bereichen maschinelles Lernen und Datenverarbeitung zu verbessern.
[Prime Counting-Funktion], die effizient parallelisiert werden kann (https://ja.wikipedia.org/wiki/%E7%B4%A0%E6%95%B0%E8%A8%88%E6%95%B0) Schauen wir uns ein Beispiel für% E9% 96% A2% E6% 95% B0) an.
package tryAny.effectiveJava;
import java.math.BigInteger;
import java.util.stream.LongStream;
public class ParallelTest1 {
// Prime-counting stream pipeline - benefits from parallelization
static long pi(long n) {
return LongStream.rangeClosed(2, n).mapToObj(BigInteger::valueOf).filter(i -> i.isProbablePrime(50)).count();
}
public static void main(String[] args) {
StopWatch sw = new StopWatch();
sw.start();
System.out.println(pi(10000000));
sw.stop();
System.out.println(sw.getTime());
}
}
Die Verarbeitung des obigen Codes dauerte ungefähr 42 Sekunden. Parallelisieren Sie dies.
package tryAny.effectiveJava;
import java.math.BigInteger;
import java.util.stream.LongStream;
import org.apache.commons.lang3.time.StopWatch;
public class ParallelTest1 {
// Prime-counting stream pipeline - benefits from parallelization
static long pi(long n) {
return LongStream.rangeClosed(2, n).parallel().mapToObj(BigInteger::valueOf).filter(i -> i.isProbablePrime(50))
.count();
}
public static void main(String[] args) {
StopWatch sw = new StopWatch();
sw.start();
System.out.println(pi(10000000));
sw.stop();
System.out.println(sw.getTime());
}
}
Dies endete in ungefähr 23 Sekunden. (Läuft auf einem 2-Core-Computer)
Wenn Sie zufällige Werte parallel generieren möchten, sollten Sie SplittableRandom anstelle von ThreadLocalRandom verwenden.