[英]Java parallelStream() with reduce() not improving performance
為了測試Java 8的流和自動並行化的新實現,我運行了以下簡單測試:
ArrayList<Integer> nums = new ArrayList<>();
for (int i=1; i<49999999; i++) nums.add(i);
int sum=0;
double begin, end;
begin = System.nanoTime();
for (Integer i : nums) sum += i;
end = System.nanoTime();
System.out.println( "1 core: " + (end-begin) );
begin = System.nanoTime();
sum = nums.parallelStream().reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println( "8 cores: " + (end-begin) );
我認為總結一系列整數將可以充分利用所有8個內核,但是輸出看起來像這樣:
1 core: 1.70552398E8
8 cores: 9.938507635E9
我知道nanoTime()在多核系統中存在問題,但是我懷疑這是問題所在,因為我的工作量還差一個數量級。
我執行的操作是否如此簡單,以至reduce()所需的開銷克服了多核的優勢?
您的流示例為每個數字有2個拆箱( Integer.sum(int,int)
)和一個裝箱(結果int
必須轉換回Integer
),而每個for循環只有一個拆箱。 因此,這兩者是不可比較的。
當您計划使用Integers進行計算時,最好使用IntStream
:
nums.stream().mapToInt(i -> i).sum();
這將為您提供類似於for循環的性能。 在我的機器上,並行流仍然較慢。
最快的替代方法是:
IntStream.rangeClosed(0, 49999999).sum();
快一個數量級,並且無需先建立列表的開銷。 當然,這只是此特殊用例的替代方案。 但它表明,重新思考現有方法而不只是“添加流”是有回報的。
要完全比較這一點,兩個操作都需要使用類似的開銷。
ArrayList<Integer> nums = new ArrayList<>();
for (int i = 1; i < 49999999; i++)
nums.add(i);
int sum = 0;
long begin, end;
begin = System.nanoTime();
sum = nums.stream().reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println("1 core: " + (end - begin));
begin = System.nanoTime();
sum = nums.parallelStream().reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println("8 cores: " + (end - begin));
這讓我着陸
1 core: 769026020
8 cores: 538805164
實際上,它對parallelStream()
更快。 (注意:我只有4個核心,但是parallelSteam()
並不總是使用您所有的核心)
另一件事是裝箱和拆箱。 有對nums.add(i)
裝箱,對進入Integer::sum
所有內容都進行裝箱,這需要兩個int
。 我將此測試轉換為數組以刪除該數組:
int[] nums = new int[49999999];
System.err.println("adding numbers");
for (int i = 1; i < 49999999; i++)
nums[i - 1] = i;
int sum = 0;
System.err.println("begin");
long begin, end;
begin = System.nanoTime();
sum = Arrays.stream(nums).reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println("1 core: " + (end - begin));
begin = System.nanoTime();
sum = Arrays.stream(nums).parallel().reduce(0, Integer::sum);
end = System.nanoTime();
System.out.println("8 cores: " + (end - begin));
這給了一個意外的時間:
1 core: 68050642
8 cores: 154591290
對於具有規則整數的線性縮減,它的速度要快得多(1-2個數量級),但對於平行縮減而言,它只有大約1/4的時間,最終變得更慢。
我不確定為什么會這樣,但這肯定很有趣!
經過一些分析,結果發現執行並行流的fork()
方法非常昂貴,因為使用了ThreadLocalRandom
,它需要網絡接口作為種子! 這非常慢,並且是parallelStream()
比stream()
慢的唯一原因!
我的一些VisualVM數據:(忽略await()
時間,這是我使用的方法,因此我可以跟蹤程序)對於第一個示例: https : //www.dropbox.com/s/z7qf2es0lxs6fvu/streams1.nps?dl= 0對於第二個示例: https : //www.dropbox.com/s/f3ydl4basv7mln5/streams2.nps?dl= 0
TL; DR:在您的Integer
情況下,它看起來像是並行獲勝,但是int
情況有一些開銷,這會使並行速度變慢。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.