JavaRush /Java-Blog /Random-DE /Parallele Operationen auf Arrays in Java 8 – Übersetzung
billybonce
Level 29
Москва

Parallele Operationen auf Arrays in Java 8 – Übersetzung

Veröffentlicht in der Gruppe Random-DE
Übersetzung des Artikels
//Parallel Array Operations in Java 8 //Von Eric Bruno, 25. März 2014 //drdobbs.com/jvm/parallel-array-operations-in-java-8/240166287 //Eric Bruno arbeitet im Finanzsektor und bloggt für die Website Dr. Dobbs.
Die neue Version von Java erleichtert die parallele Interaktion mit Arrays – was zu einer deutlich verbesserten Leistung bei minimalem Codierungsaufwand führt. Jetzt veröffentlicht Oracle Java SE 8 – was sprachlich einen gewaltigen Fortschritt darstellt. Eine der wichtigen Funktionen dieser Version ist die verbesserte Parallelität, von der einige in der Basisklasse java.util.Arrays enthalten sind. Dieser Klasse wurden neue Methoden hinzugefügt, die ich in diesem Artikel beschreiben werde. Einige davon werden in einer weiteren neuen Funktion von JDK8 verwendet – Lambdas. Aber kommen wir zur Sache.
Arrays.paralellSort()
Viele Funktionen von parallelSort basieren auf einem parallelen Merge-Sortieralgorithmus, der ein Array rekursiv in Teile aufteilt, diese sortiert und sie dann gleichzeitig wieder zu einem endgültigen Array zusammenfügt. Die Verwendung dieser Methode anstelle der vorhandenen, sequentiellen Arrays.sort-Methode führt zu einer verbesserten Leistung und Effizienz beim Sortieren großer Arrays. Der folgende Code verwendet beispielsweise sequentielles sort() und paralleles parallelSort(), um dasselbe Datenarray zu sortieren: public class ParallelSort { public static void main(String[] args) { ParallelSort mySort = new ParallelSort(); int[] src = null; System.out.println("\nSerial sort:"); src = mySort.getData(); mySort.sortIt(src, false); System.out.println("\nParallel sort:"); src = mySort.getData(); mySort.sortIt(src, true); } public void sortIt(int[] src, boolean parallel) { try { System.out.println("--Array size: " + src.length); long start = System.currentTimeMillis(); if ( parallel == true ) { Arrays.parallelSort(src); } else { Arrays.sort(src); } long end = System.currentTimeMillis(); System.out.println( "--Elapsed sort time: " + (end-start)); } catch ( Exception e ) { e.printStackTrace(); } } private int[] getData() { try { File file = new File("src/parallelsort/myimage.png"); BufferedImage image = ImageIO.read(file); int w = image.getWidth(); int h = image.getHeight(); int[] src = image.getRGB(0, 0, w, h, null, 0, w); int[] data = new int[src.length * 20]; for ( int i = 0; i < 20; i++ ) { System.arraycopy( src, 0, data, i*src.length, src.length); } return data; } catch ( Exception e ) { e.printStackTrace(); } return null; } } Zum Testen habe ich die Rohdaten aus dem Bild in das Array geladen, das 46.083.360 Bytes benötigte (und Ihre Daten hängen von den Bildern ab). die Sie verwenden werden). Die sequentielle Sortiermethode benötigte fast 3.000 Millisekunden, um das Array auf meinem 4-Core-Laptop zu sortieren, während die parallele Sortiermethode höchstens etwa 700 Millisekunden benötigte. Stimmen Sie zu, es kommt nicht oft vor, dass ein neues Sprachupdate die Klassenleistung um das Vierfache verbessert.
Arrays.parallelPrefix()
Die parallelPrefix-Methode wendet eine bestimmte mathematische Funktion gemeinsam auf die Elemente eines Arrays an und verarbeitet die Ergebnisse innerhalb des Arrays parallel. Dies ist auf moderner Multi-Core-Hardware wesentlich effizienter als sequentielle Vorgänge auf großen Arrays. Es gibt viele Implementierungen dieser Methode für verschiedene Grundtypen von Datenoperationen (z. B. IntBinaryOperator, DoubleBinaryOperator, LongBinaryOperator usw.) sowie für verschiedene Arten mathematischer Operatoren. Hier ist ein Beispiel für paralleles Array-Stacking mit demselben großen Array wie im vorherigen Beispiel, das auf meinem 4-Core-Laptop in etwa 100 Millisekunden abgeschlossen war. public class MyIntOperator implements IntBinaryOperator { @Override public int applyAsInt(int left, int right) { return left+right; } } public void accumulate() { int[] src = null; // accumulate test System.out.println("\nParallel prefix:"); src = getData(); IntBinaryOperator op = new ParallelSort.MyIntOperator(); long start = System.currentTimeMillis(); Arrays.parallelPrefix(src, new MyIntOperator()); long end = System.currentTimeMillis(); System.out.println("--Elapsed sort time: " + (end-start)); } ... }
Arrays.parallelSetAll()
Die neue parallelSetAll()-Methode erstellt ein Array und setzt jedes Array-Element auf einen Wert entsprechend der Funktion, die diese Werte generiert hat, wobei Parallelität zur Verbesserung der Effizienz verwendet wird. Diese Methode basiert auf Lambdas (in anderen Sprachen „Abschlüsse“ genannt) (und ja, das ist der Fehler des Autors, denn Lambdas und Abschlüsse sind verschiedene Dinge) , und das sind eine weitere neue Funktion von JDK8, die wir in zukünftigen Artikeln besprechen werden. Es genügt zu beachten, dass Lambdas, deren Syntax durch den ->-Operator leicht zu erkennen ist, eine Operation auf der rechten Seite nach dem Pfeil für alle an sie übergebenen Elemente ausführen. Im folgenden Codebeispiel wird die Aktion für jedes Element im Array ausgeführt, indiziert durch i. Array.parallelSetAll() generiert Array-Elemente. Der folgende Code füllt beispielsweise ein großes Array mit zufälligen ganzzahligen Werten: public void createLargeArray() { Integer[] array = new Integer[1024*1024*4]; // 4M Arrays.parallelSetAll( array, i -> new Integer( new Random().nextInt())); } Um einen komplexeren Array-Elementgenerator zu erstellen (z. B. einen, der Werte basierend auf Messwerten von Sensoren in der realen Welt generiert), können Sie Code ähnlich dem folgenden verwenden Folgendes: public void createLargeArray() { Integer[] array = new Integer[1024*1024*4]; // 4M Arrays.parallelSetAll( array, i -> new Integer( customGenerator(getNextSensorValue()))); } public int customGenerator(int arg){ return arg + 1; // some fancy formula here... } public int getNextSensorValue() { // Just random for illustration return new Random().nextInt(); } Wir beginnen mit getNextSensorValue, das in Wirklichkeit den Sensor (z. B. ein Thermometer) auffordert, seinen aktuellen Wert zurückzugeben. Hier wird beispielhaft ein Zufallswert generiert. Die folgende Methode „customGenerator()“ generiert ein Array von Elementen mithilfe der ausgewählten Logik basierend auf dem von Ihnen ausgewählten Fall. Hier ist eine kleine Ergänzung, aber für reale Fälle wäre es etwas komplizierter.
Was ist Spliterator?
Eine weitere Ergänzung zur Arrays-Klasse, die Parallelität und Lambdas nutzt, ist der Spliterator, der zum Iterieren und Teilen eines Arrays verwendet wird. Seine Wirkung ist nicht auf Arrays beschränkt – es funktioniert auch gut für Collection-Klassen und IO-Kanäle. Spliteratoren funktionieren, indem sie ein Array automatisch in verschiedene Teile aufteilen, und ein neuer Spliterator wird installiert, um Operationen an diesen verknüpften Subarrays durchzuführen. Sein Name setzt sich aus Iterator zusammen, der seine Arbeit der Moving-Iteration in Teile „aufteilt“. Unter Verwendung derselben Daten können wir wie folgt eine geteilte Aktion für unser Array ausführen: Das Ausführen von Aktionen für die Daten auf diese Weise nutzt die Parallelität aus. Sie können auch Splitter-Parameter festlegen, z. B. die Mindestgröße jedes Subarrays. public void spliterate() { System.out.println("\nSpliterate:"); int[] src = getData(); Spliterator spliterator = Arrays.spliterator(src); spliterator.forEachRemaining( n -> action(n) ); } public void action(int value) { System.out.println("value:"+value); // Perform some real work on this data here... }
Stream - Verarbeitung
Schließlich können Sie aus einem Array ein Stream-Objekt erstellen, das die parallele Verarbeitung einer Stichprobe von Daten als Ganzes ermöglicht, verallgemeinert in einer Stream-Sequenz. Der Unterschied zwischen einer Datensammlung und einem Stream aus dem neuen JDK8 besteht darin, dass Sammlungen es Ihnen ermöglichen, mit Elementen einzeln zu arbeiten, während dies bei einem Stream nicht möglich ist. Bei Sammlungen können Sie beispielsweise Elemente hinzufügen, entfernen und in der Mitte einfügen. Mit einer Stream-Sequenz können Sie nicht einzelne Elemente eines Datensatzes manipulieren, sondern Funktionen für die Daten als Ganzes ausführen. Sie können so nützliche Operationen durchführen wie das Extrahieren nur bestimmter Werte (ohne Berücksichtigung von Wiederholungen) aus einer Menge, Datentransformationsoperationen, das Finden des Minimums und Maximums eines Arrays, Map-Reduction-Funktionen (verwendet beim verteilten Rechnen) und andere mathematische Operationen. Das folgende einfache Beispiel verwendet Parallelität, um ein Datenarray parallel zu verarbeiten und die Elemente zu summieren. public void streamProcessing() { int[] src = getData(); IntStream stream = Arrays.stream(src); int sum = stream.sum(); System.out.println("\nSum: " + sum); }
Abschluss
Java 8 wird definitiv eines der nützlichsten Updates der Sprache sein. Die hier erwähnten parallelen Funktionen, Lambdas und viele andere Erweiterungen werden Gegenstand weiterer Java 8-Rezensionen auf unserer Website sein.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION