亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Java 8:Streams vs Collections的性能

Java 8:Streams vs Collections的性能

眼眸繁星 2019-08-30 15:53:40
我是Java 8的新手。我仍然不深入了解API,但我已經做了一個小的非正式基準測試來比較新Streams API與優秀舊Collections的性能。測試包括過濾一個列表Integer,并為每個偶數計算平方根并將其存儲在結果List中Double。這是代碼:    public static void main(String[] args) {        //Calculating square root of even numbers from 1 to N               int min = 1;        int max = 1000000;        List<Integer> sourceList = new ArrayList<>();        for (int i = min; i < max; i++) {            sourceList.add(i);        }        List<Double> result = new LinkedList<>();        //Collections approach        long t0 = System.nanoTime();        long elapsed = 0;        for (Integer i : sourceList) {            if(i % 2 == 0){                result.add(Math.sqrt(i));            }        }        elapsed = System.nanoTime() - t0;               System.out.printf("Collections: Elapsed time:\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));        //Stream approach        Stream<Integer> stream = sourceList.stream();               t0 = System.nanoTime();        result = stream.filter(i -> i%2 == 0).map(i -> Math.sqrt(i)).collect(Collectors.toList());        elapsed = System.nanoTime() - t0;               System.out.printf("Streams: Elapsed time:\t\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));        //Parallel stream approach        stream = sourceList.stream().parallel();                t0 = System.nanoTime();        result = stream.filter(i -> i%2 == 0).map(i -> Math.sqrt(i)).collect(Collectors.toList());        elapsed = System.nanoTime() - t0;               System.out.printf("Parallel streams: Elapsed time:\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));          }.以下是雙核機器的結果:    Collections: Elapsed time:        94338247 ns   (0,094338 seconds)    Streams: Elapsed time:           201112924 ns   (0,201113 seconds)    Parallel streams: Elapsed time:  357243629 ns   (0,357244 seconds)對于這個特定的測試,流的速度大約是集合的兩倍,并行性沒有幫助(或者我使用錯誤的方式?)。問題:這個測試公平嗎?我犯了什么錯嗎?流比收集慢嗎?有誰在這方面做了一個很好的正式基準?我應該采取哪種方法?
查看完整描述

3 回答

?
猛跑小豬

TA貢獻1858條經驗 獲得超8個贊

LinkedList使用迭代器停止使用除列表中間的大量刪除之外的任何內容。


停止手動編寫基準測試代碼,使用JMH。


適當的基準:


@OutputTimeUnit(TimeUnit.NANOSECONDS)

@BenchmarkMode(Mode.AverageTime)

@OperationsPerInvocation(StreamVsVanilla.N)

public class StreamVsVanilla {

    public static final int N = 10000;


    static List<Integer> sourceList = new ArrayList<>();

    static {

        for (int i = 0; i < N; i++) {

            sourceList.add(i);

        }

    }


    @Benchmark

    public List<Double> vanilla() {

        List<Double> result = new ArrayList<>(sourceList.size() / 2 + 1);

        for (Integer i : sourceList) {

            if (i % 2 == 0){

                result.add(Math.sqrt(i));

            }

        }

        return result;

    }


    @Benchmark

    public List<Double> stream() {

        return sourceList.stream()

                .filter(i -> i % 2 == 0)

                .map(Math::sqrt)

                .collect(Collectors.toCollection(

                    () -> new ArrayList<>(sourceList.size() / 2 + 1)));

    }

}

結果:


Benchmark                   Mode   Samples         Mean   Mean error    Units

StreamVsVanilla.stream      avgt        10       17.588        0.230    ns/op

StreamVsVanilla.vanilla     avgt        10       10.796        0.063    ns/op

正如我預期的那樣,流實現速度相當慢。JIT能夠內聯所有lambda內容,但不會像vanilla版本那樣生成完美簡潔的代碼。


通常,Java 8流不是魔術。他們無法加速已經很好實現的東西(可能是普通的迭代或Java 5的 - 每個語句都替換為Iterable.forEach()和Collection.removeIf()調用)。流更多的是編碼方便性和安全性。方便 - 速度權衡在這里工作。


查看完整回答
反對 回復 2019-08-30
?
SMILET

TA貢獻1796條經驗 獲得超4個贊

1)您使用基準測試時間不到1秒。這意味著副作用會對您的結果產生強烈影響。所以,我增加了你的任務10次


    int max = 10_000_000;

并運行你的基準。我的結果:


Collections: Elapsed time:   8592999350 ns  (8.592999 seconds)

Streams: Elapsed time:       2068208058 ns  (2.068208 seconds)

Parallel streams: Elapsed time:  7186967071 ns  (7.186967 seconds)

沒有edit(int max = 1_000_000)結果


Collections: Elapsed time:   113373057 ns   (0.113373 seconds)

Streams: Elapsed time:       135570440 ns   (0.135570 seconds)

Parallel streams: Elapsed time:  104091980 ns   (0.104092 seconds)

這就像你的結果:流比收集慢。結論:流初始化/值傳輸花費了大量時間。


2)增加任務流后變得更快(沒關系),但并行流仍然太慢。怎么了?注意:你有collect(Collectors.toList())命令。收集單個集合實質上會在并發執行時引入性能瓶頸和開銷??梢酝ㄟ^替換來估計開銷的相對成本


collecting to collection -> counting the element count

對于流,它可以通過collect(Collectors.counting())。我得到了結果:


Collections: Elapsed time:   41856183 ns    (0.041856 seconds)

Streams: Elapsed time:       546590322 ns   (0.546590 seconds)

Parallel streams: Elapsed time:  1540051478 ns  (1.540051 seconds)

那是一項艱巨的任務!(int max = 10000000)結論:收集物品需要花費大部分時間。最慢的部分是添加到列表中。BTW,簡單ArrayList用于Collectors.toList()。


查看完整回答
反對 回復 2019-08-30
?
繁星coding

TA貢獻1797條經驗 獲得超4個贊

public static void main(String[] args) {

    //Calculating square root of even numbers from 1 to N       

    int min = 1;

    int max = 10000000;


    List<Integer> sourceList = new ArrayList<>();

    for (int i = min; i < max; i++) {

        sourceList.add(i);

    }


    List<Double> result = new LinkedList<>();



    //Collections approach

    long t0 = System.nanoTime();

    long elapsed = 0;

    for (Integer i : sourceList) {

        if(i % 2 == 0){

            result.add( doSomeCalculate(i));

        }

    }

    elapsed = System.nanoTime() - t0;       

    System.out.printf("Collections: Elapsed time:\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));



    //Stream approach

    Stream<Integer> stream = sourceList.stream();       

    t0 = System.nanoTime();

    result = stream.filter(i -> i%2 == 0).map(i -> doSomeCalculate(i))

            .collect(Collectors.toList());

    elapsed = System.nanoTime() - t0;       

    System.out.printf("Streams: Elapsed time:\t\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));



    //Parallel stream approach

    stream = sourceList.stream().parallel();        

    t0 = System.nanoTime();

    result = stream.filter(i -> i%2 == 0).map(i ->  doSomeCalculate(i))

            .collect(Collectors.toList());

    elapsed = System.nanoTime() - t0;       

    System.out.printf("Parallel streams: Elapsed time:\t %d ns \t(%f seconds)%n", elapsed, elapsed / Math.pow(10, 9));      

}


static double doSomeCalculate(int input) {

    for(int i=0; i<100000; i++){

        Math.sqrt(i+input);

    }

    return Math.sqrt(input);

}

我稍微更改了代碼,在我的mac book pro上運行了8個內核,得到了一個合理的結果:


收藏:經歷時間:1522036826 ns(1.522037秒)


流:經過的時間:4315833719 ns(4.315834秒)


并行流:經過時間:261152901 ns(0.261153秒)


查看完整回答
反對 回復 2019-08-30
  • 3 回答
  • 0 關注
  • 881 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號