JavaRush /Blog Java /Random-ES /Operaciones paralelas en matrices en Java 8 - traducción
billybonce
Nivel 29
Москва

Operaciones paralelas en matrices en Java 8 - traducción

Publicado en el grupo Random-ES
traducción del artículo
//Operaciones de matriz paralela en Java 8 //Por Eric Bruno, 25 de marzo de 2014 //drdobbs.com/jvm/parallel-array-operatives-in-java-8/240166287 //Eric Bruno trabaja en el sector financiero y blogs para el sitio web dr. Dobb's.
La nueva versión de Java facilita la interacción con matrices en paralelo, lo que resulta en un rendimiento significativamente mejorado con un mínimo de codificación. Ahora Oracle lanza Java SE 8, lo que supone un gran paso adelante en términos de lenguaje. Una de las características importantes de esta versión es la concurrencia mejorada, parte de la cual aparece en la clase base java.util.Arrays. Se han agregado nuevos métodos a esta clase, que describiré en este artículo. Algunos de estos se utilizan en otra característica nueva de JDK8: lambdas. Pero pongámonos manos a la obra.
Arrays.paralellSort()
Muchas de las funciones de paraleloSort se basan en un algoritmo de ordenación por combinación paralela que divide recursivamente una matriz en partes, las clasifica y luego las recombina simultáneamente en una matriz final. Su uso en lugar del método secuencial Arrays.sort existente da como resultado un mejor rendimiento y eficiencia al ordenar matrices grandes. Por ejemplo, el siguiente código utiliza ordenación secuencial() y ordenación paralela paralela() para ordenar la misma matriz de datos: 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; } } Para probar, cargué los datos sin procesar de la imagen en la matriz, lo que tomó 46,083,360 bytes (y el suyo dependerá de las imágenes). que utilizarás). El método de clasificación secuencial tomó casi 3000 milisegundos para ordenar la matriz en mi computadora portátil de 4 núcleos, mientras que el método de clasificación en paralelo tomó alrededor de 700 milisegundos como máximo. De acuerdo, no sucede a menudo que una nueva actualización de idioma mejore el rendimiento de la clase 4 veces.
Arrays.parallelPrefix()
El método paraleloPrefix aplica una función matemática específica a los elementos de una matriz colectivamente, procesando los resultados dentro de la matriz en paralelo. Esto es mucho más eficiente en hardware multinúcleo moderno, en comparación con las operaciones secuenciales en matrices grandes. Existen muchas implementaciones de este método para diferentes tipos básicos de operaciones de datos (por ejemplo, IntBinaryOperator, DoubleBinaryOperator, LongBinaryOperator, etc.), así como para diferentes tipos de operadores matemáticos. A continuación se muestra un ejemplo de apilamiento de matrices en paralelo utilizando la misma matriz grande que en el ejemplo anterior, que se completó en aproximadamente 100 milisegundos en mi computadora portátil de 4 núcleos. 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()
Новый метод parallelSetAll() создает массив и устанавливает каждому элементу массива significado в соответствии с генерирующей эти значения функцией, используя параллельность для повышения эфективности. Этот метод основан на лямбдах(называемых "замыканиями"(closures) в других языках) (и, да, тут ошибка автора, ибо лямбды и замыкания это разные вещи) , и которые являются еще одной новинкой JDK8, которую мы обсудим в будущих статьях. Будет достаточно заметить, лямбды, чей синтаксис легко опознать по оператору ->, производящему операцию над правой частью после стрелки для всех переданных ему элементов. В примере códigoа, представленном ниже - действие производится для каждого elemento в массиве, проиндексированного по i. Array.parallelSetAll() генерирует элементы массива. Например, следующий código заполняет большой массив случайными integer-значениями: public void createLargeArray() { Integer[] array = new Integer[1024*1024*4]; // 4M Arrays.parallelSetAll( array, i -> new Integer( new Random().nextInt())); } Для создания более сложного генератора элементов массива(например, такого что генерировал бы значения на основе считывания с датчиков из реального мира), можно использовать código близкий к следующему: 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(); } Мы начнем с getNextSensorValue, который в реальности будет запрашивать датчик(например термометр) вернуть ему текущее significado. Здесь же для примера генерируется случайное significado. Следующий customGenerator() метод генерирует массив элементов с использованием выбранной логики на основе выбранного вами случая. Вот небольшое дополнение, но для реальных случаев, тут было бы что-нибудь посложнее.
Qué такое Spliterator?
Другое дополнение к классу Arrays, которое использует параллельность и лямбды - это Spliterator, который используется для итерации и разделения массива. Его действие не ограничено только массивами - он также хорошо работает и для классов Collection и IO каналов. Spliterator'ы работают на основе автоматического разбиения массива на различные части, а новый Spliterator устанавливается для того чтобы производить операции над этими связанными подмассивами. Его название составленно из Iterator(итератора), который "разделяет"(splits) его работу по перемещению-итерации на части. Используя наши, всё те же, данные, мы можем произвести раздельноитерированное(splititerated) действие над нашим массивом следующим образом: 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... } Выполнение действий над данными таким образом использует плюсы параллельности. Вы можете также задать параметры сплититератора, такие Cómo минимальный размер каждого подмассива.
Stream - обработка
Finalmente, desde un Array, se puede crear un objeto Stream, que permite el procesamiento paralelo de una muestra de datos en su conjunto, generalizados en una secuencia de flujo. La diferencia entre una Colección de datos y un Stream del nuevo JDK8 es que las colecciones te permiten trabajar con elementos individualmente cuando un Stream no lo hace. Por ejemplo, con las colecciones, puedes agregar elementos, eliminarlos e insertarlos en el medio. Una secuencia Stream no le permite manipular elementos individuales de un conjunto de datos, sino que le permite realizar funciones sobre los datos en su conjunto. Puede realizar operaciones tan útiles como extraer solo valores específicos (ignorando repeticiones) de un conjunto, operaciones de transformación de datos, encontrar el mínimo y el máximo de una matriz, funciones de reducción de mapas (utilizadas en computación distribuida) y otras operaciones matemáticas. El siguiente ejemplo sencillo utiliza la concurrencia para procesar una matriz de datos en paralelo y sumar los elementos. public void streamProcessing() { int[] src = getData(); IntStream stream = Arrays.stream(src); int sum = stream.sum(); System.out.println("\nSum: " + sum); }
Conclusión
Java 8 definitivamente será una de las actualizaciones más útiles del lenguaje. Las características paralelas mencionadas aquí, lambdas y muchas otras extensiones serán el tema de otras revisiones de Java 8 en nuestro sitio.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION