JavaRush /Java блогы /Random-KK /Java 8-де массивтерге параллельді амалдар – аударма
billybonce
Деңгей
Москва

Java 8-де массивтерге параллельді амалдар – аударма

Топта жарияланған
мақаланың аудармасы
//Java 8 жүйесіндегі параллельді массив операциялары //Эрик Бруно, 2014 жылғы 25 наурыз //drdobbs.com/jvm/parallel-array-operations-in-java-8/240166287 //Эрик Бруно қаржы секторында және блогтарда жұмыс істейді веб-сайт үшін Dr. Доббтың.
Java-ның жаңа шығарылымы массивтермен параллельді өзара әрекеттесуді жеңілдетеді - нәтижесінде ең аз codeтаумен өнімділік айтарлықтай жақсарды. Қазір Oracle Java SE 8 нұсқасын шығарады - бұл тіл тұрғысынан алға жасалған үлкен қадам. Бұл шығарылымның маңызды мүмкіндіктерінің бірі - кейбіреулері java.util.Arrays негізгі сыныбында пайда болатын жақсартылған параллельдік. Бұл сыныпқа жаңа әдістер қосылды, мен оларды осы мақалада сипаттаймын. Олардың кейбіреулері JDK8 тағы бір жаңа мүмкіндікте - ламбдаларда қолданылады. Бірақ іске кірісейік.
Arrays.paralellSort()
ParallelSort мүмкіндіктерінің көпшілігі массивді бөліктерге рекурсивті түрде бөлетін, сұрыптайтын, содан кейін оларды бір уақытта соңғы массивке қайта біріктіретін параллельді біріктіру сұрыптау алгоритміне негізделген. Оны бар, дәйекті Arrays.sort әдісінің орнына пайдалану үлкен массивтерді сұрыптау кезінде өнімділік пен тиімділікті арттырады. Мысалы, төмендегі code бірдей деректер массивін сұрыптау үшін дәйекті сұрыптау() және параллельді parallelSort() пайдаланады: 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; } } Тексеру үшін мен кескіннен 46 083 360 byte алған (және сіздікі кескіндерге байланысты болады) алапқа бастапқы деректерді жүктедім. сіз оны пайдаланасыз). Тізбектелген сұрыптау әдісі 4 ядролы ноутбугымда массивді сұрыптау үшін 3000 миллисекундқа жуық уақытты алды, ал параллельді сұрыптау әдісіне ең көбі 700 миллисекунд қажет болды. Келісіңіз, жаңа тіл жаңартуы сынып өнімділігін 4 есе жақсартады.
Arrays.parallelPrefix()
ParallelPrefix әдісі жиымдағы нәтижелерді параллельді түрде өңдей отырып, көрсетілген математикалық функцияны жиынтықта массив элементтеріне қолданады. Бұл үлкен массивтердегі дәйекті операциялармен салыстырғанда қазіргі көп ядролы жабдықта әлдеқайда тиімді. Деректер операцияларының әртүрлі негізгі түрлері үшін (мысалы, IntBinaryOperator, DoubleBinaryOperator, LongBinaryOperator және т.б.), сондай-ақ математикалық операторлардың әртүрлі типтері үшін бұл әдістің көптеген іске асырулары бар. Міне, менің 4 ядролы ноутбугімде шамамен 100 миллисекундта аяқталатын алдыңғы мысалдағыдай үлкен массив арқылы параллельді массивтерді жинақтау мысалы. 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() создает массив и устанавливает каждому элементу массива meaning в соответствии с генерирующей эти значения функцией, используя параллельность для повышения эфективности. Этот метод основан на лямбдах(называемых "замыканиями"(closures) в других языках) (и, да, тут ошибка автора, ибо лямбды и замыкания это разные вещи) , и которые являются еще одной новинкой JDK8, которую мы обсудим в будущих статьях. Будет достаточно заметить, лямбды, чей синтаксис легко опознать по оператору ->, производящему операцию над правой частью после стрелки для всех переданных ему элементов. В примере codeа, представленном ниже - действие производится для каждого element в массиве, проиндексированного по i. Array.parallelSetAll() генерирует элементы массива. Например, следующий code заполняет большой массив случайными integer-значениями: public void createLargeArray() { Integer[] array = new Integer[1024*1024*4]; // 4M Arrays.parallelSetAll( array, i -> new Integer( new Random().nextInt())); } Для создания более сложного генератора элементов массива(например, такого что генерировал бы значения на основе считывания с датчиков из реального мира), можно использовать code близкий к следующему: 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, который в реальности будет запрашивать датчик(например термометр) вернуть ему текущее meaning. Здесь же для примера генерируется случайное meaning. Следующий customGenerator() метод генерирует массив элементов с использованием выбранной логики на основе выбранного вами случая. Вот небольшое дополнение, но для реальных случаев, тут было бы что-нибудь посложнее.
What такое 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... } Выполнение действий над данными таким образом использует плюсы параллельности. Вы можете также задать параметры сплититератора, такие How минимальный размер каждого подмассива.
Stream - обработка
Соңында, массивтен ағын тізбегіне жалпыланған жалпы деректер үлгісінде параллель өңдеуге мүмкіндік беретін Stream нысанын жасауға болады. Деректер жинағы мен жаңа JDK8 ағынының арасындағы айырмашылық ағынмен жұмыс істемейтін кезде жинақтар элементтермен жеке жұмыс істеуге мүмкіндік береді. Мысалы, жинақтармен элементтерді қосуға, оларды жоюға және ортасына кірістіруге болады. Ағын тізбегі деректер жиынындағы жеке элементтерді басқаруға мүмкіндік бермейді, бірақ оның орнына тұтастай деректерде функцияларды орындауға мүмкіндік береді. Жиыннан тек нақты мәндерді шығару (қайталауларды елемеу), деректерді түрлендіру операциялары, массивтің минималды және максимумын табу, картаны азайту функциялары (үлестірілген есептеулерде қолданылады) және басқа да математикалық операциялар сияқты пайдалы операцияларды орындауға болады. Келесі қарапайым мысал деректер жиымын параллельді өңдеу және элементтерді қосу үшін параллельділікті пайдаланады. public void streamProcessing() { int[] src = getData(); IntStream stream = Arrays.stream(src); int sum = stream.sum(); System.out.println("\nSum: " + sum); }
Қорытынды
Java 8 міндетті түрде тілге арналған ең пайдалы жаңартулардың бірі болады. Мұнда айтылған параллель мүмкіндіктер, ламбдалар және басқа да көптеген кеңейтімдер біздің сайттағы басқа Java 8 шолуларының тақырыбы болады.
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION