簡體   English   中英

帶有reduce()的Java parallelStream()不能提高性能

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM