JavaRush /Java Blog /Random-TW /Java 8 中陣列的平行操作 - 翻譯
billybonce
等級 29
Москва

Java 8 中陣列的平行操作 - 翻譯

在 Random-TW 群組發布
文章翻譯
//Java 8 中的平行數組操作//作者:Eric Bruno,2014 年3 月25 日//drdobbs.com/jvm/parallel-array-operations-in-java-8/240166287 //Eric Bruno 在金融領域工作並撰寫部落格對於網站 Dr. 多布的。
新版本的 Java 使與陣列的並行互動變得更加容易,從而以最少的編碼顯著提高了效能。現在,Oracle 發布了 Java SE 8——這在語言方面是一個巨大的進步。此版本的重要功能之一是改進的並發性,其中一些功能出現在 java.util.Arrays 基底類別中。此類中新增了新方法,我將在本文中對其進行描述。其中一些用於 JDK8 的另一個新功能 - lambdas。但讓我們言歸正傳吧。
Arrays.parallSort()
ParallelSort 的許多功能都基於平行合併排序演算法,該演算法遞歸地將陣列拆分為多個部分,對它們進行排序,然後將它們同時重新組合成最終數組。使用它來取代現有的順序 Arrays.sort 方法可以提高對大型陣列進行排序時的效能和效率。 例如,下面的程式碼使用順序排序()和並行並行排序()對相同的資料數組進行排序:為了測試,我將圖像 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 位元組(您的資料將取決於圖像您將使用它)。在我的 4 核心筆記型電腦上,順序排序方法花了近 3,000 毫秒對陣列進行排序,而平行排序方法最多需要大約 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()方法建立一個數組,並根據產生這些值的函數將每個數組元素設為一個值,使用並行性來提高效率。該方法基於 lambda(在其他語言中稱為“閉包”) (並且,是的,這是作者的錯誤,因為 lambda 和閉包是不同的東西) ,這是 JDK8 的另一個新特性,我們將在以後的文章中討論。只要注意 lambda 的語法很容易透過 -> 運算子識別,它會在箭頭後面的右側對傳遞給它的所有元素執行操作。在下面的程式碼範例中,對數組中的每個元素執行該操作,索引為 i。Array.parallelSetAll() 產生陣列元素。例如,以下程式碼用隨機整數值填充一個大數組: public void createLargeArray() { Integer[] array = new Integer[1024*1024*4]; // 4M Arrays.parallelSetAll( array, i -> new Integer( new Random().nextInt())); } 要創建一個更複雜的數組元素生成器(例如,根據實際感測器的讀數生成值的生成器),您可以使用類似於以下的程式碼我們將從 getNextSensorValue開始 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(); } ,實際上,它會要求感測器(例如溫度計)返回其當前值。這裡,作為範例,產生隨機值。以下 customGenerator() 方法根據您選擇的情況使用選定的邏輯產生元素數組。這是一個小的補充,但對於實際情況,情況會更複雜。
什麼是分裂器?
Arrays 類別中另一個使用並發和 lambda 的附加功能是 Spliterator,它用於迭代和分割陣列。它的作用不僅限於陣列——它也適用於 Collection 類別和 IO 通道。Spliterator 的工作原理是自動將陣列拆分為不同的部分,並安裝新的 Spliterator 來對這些連結的子數組執行操作。它的名字由 Iterator 組成,它將移動迭代的工作「分割」成多個部分。使用相同的數據,我們可以對數組執行分割操作,如下所示: 以這種方式對資料執行操作可以利用並行性。您也可以設定分割器參數,例如每個子陣列的最小大小。 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 對象,它允許對整個資料樣本進行並行處理,並概括為流序列。資料集合和新 JDK8 中的流之間的區別在於,集合允許您單獨處理元素,而流序列則不允許。例如,對於集合,您可以新增元素、刪除元素以及將它們插入中間。流序列不允許您操作資料集中的單一元素,而是允許您對整個資料執行函數。您可以執行一些有用的操作,例如從集合中僅提取特定值(忽略重複)、資料轉換操作、查找陣列的最小值和最大值、map-reduce 函數(用於分散式計算)以及其他數學運算。以下簡單範例使用並行來並行處理資料數組並對元素求和。 public void streamProcessing() { int[] src = getData(); IntStream stream = Arrays.stream(src); int sum = stream.sum(); System.out.println("\nSum: " + sum); }
結論
Java 8 肯定會是該語言最有用的更新之一。這裡提到的平行功能、lambda 和許多其他擴充功能將成為我們網站上其他 Java 8 評論的主題。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION