通过简单打印列表进行Java 8速度测试

时间:2015-02-04 10:14:12

标签: java performance arraylist lambda

import java.util.ArrayList;
import java.util.List;


public class HowFastMulticoreProgramming {

    public static void main(String[] args) {

        //Produce Date
        List<String> data=new ArrayList<String>();
        for(int i=0;i<10000;i++){
            data.add(""+i);
        }


        /*Style Java 1.4*/
        long beforeStartJDK14=System.currentTimeMillis();
        for (int i = 0; i < data.size(); i++) {
            System.out.println(data.get(i));
        }
        long afterPrintJDK14=System.currentTimeMillis();

        /*Style Java 1.5*/
        long beforeStartJDK15=System.currentTimeMillis();
        for (String s : data) {
            System.out.println(s);
        }
        long afterPrintJDK15=System.currentTimeMillis();

        long beforeStartJDK18=System.currentTimeMillis();
        data.parallelStream().forEach(string-> System.out.println(string));
        long afterPrintJDK18=System.currentTimeMillis();

        System.out.println("Milis Need JDK 1.4 : "+(afterPrintJDK14-beforeStartJDK14));
        System.out.println("Milis Need JDK 1.5 : "+(afterPrintJDK15-beforeStartJDK15));
        System.out.println("Milis Need JDK 1.8 : "+(afterPrintJDK18-beforeStartJDK18));

    }
}

我有3种样式可以打印List(基于JDK版本)。但每种风格都需要时间来完成。实际上风格jdk 8与lambdas ..任何风格都需要更大。 怎么来的?

这是我运行此代码所得到的; 时间Milis需要JDK 1.4:85 时间Milis需要JDK 1.5:76 时间Milis需要JDK 1.8:939

我希望有人能回答这个问题。

2 个答案:

答案 0 :(得分:4)

这种比较完全没有意义。

首先,前两个变体完全由I / O时间控制。任何输出的任何循环通常都是。迭代的方式可能会产生噪音。 I / O很慢。

但它并不像你在第三个版本中做的那么慢。在第三个变体中,使用parallelStream(),它调用Java8的join / fork机制。您正在生成多个线程(可能与您拥有CPU核心数一样多)。您正在分配任务以在这些线程上编写列表元素。然后,您将从每个线程写入相同的流序列化其操作,即在您完成创建线程和分发任务的所有工作之后,你现在仍然只做一件事,加上你现在也会产生大量的同步开销。

如果你想做一个有趣的比较,你需要将数据转换成其他一些数据,你需要对每个项目进行非平凡(但不是同步)的工作,这样任务管理开销就不会淹没计算时间。

在此期间,请尝试使用stream()代替parallelStream()。这应该将时间缩短到大致相当于其他两个变体的时间。但这并没有使它更有意义。

答案 1 :(得分:1)

免责声明:您正在进行微基准测试和微基准测试are hard to do。我确信下面稍微改变的代码本身就有足够的问题。

parallelStream()需要一些启动时间,并且您有多个Thread带来的开销。

另一个问题是,你正在为每个项目做System.out.println() - 它是IO,所以除了迭代之外你还要测量很多。当您从多个线程访问一个流(System.out)时,这尤其是一个问题。

如果你删除你的打印语句,JVM可能只是跳过循环,这就是为什么我只是将每个元素添加到一个总和。应该非常快,并且不会被优化掉。

当运行以下示例,列表大小为100000000(创建大约需要一分钟)时,我得到以下结果:

Milis Need JDK 1.4 : 190
Milis Need JDK 1.5 : 681
Milis Need JDK 1.8 : 198

我的代码:

@Test
public void testIterationSpeed() {
    List<Integer> data = new ArrayList<>();
    for (int i = 0; i < 100000000; i++) {
        data.add(i);
    }

    /*Style Java 1.4*/
    long dummySum = 0;
    long beforeStartJDK14 = System.currentTimeMillis();

    for (int i = 0; i < data.size(); i++) {
        dummySum += data.get(i);
    }
    long afterPrintJDK14 = System.currentTimeMillis();

    /*Style Java 1.5*/
    dummySum = 0;
    long beforeStartJDK15 = System.currentTimeMillis();

    for (Integer i : data) {
        dummySum += i;
    }
    long afterPrintJDK15 = System.currentTimeMillis();

    /* Java 1.8 */
    long beforeStartJDK18 = System.currentTimeMillis();
    data.parallelStream().mapToLong(i -> i).sum();
    long afterPrintJDK18 = System.currentTimeMillis();

    System.out.println("Milis Need JDK 1.4 : " + (afterPrintJDK14 - beforeStartJDK14));
    System.out.println("Milis Need JDK 1.5 : " + (afterPrintJDK15 - beforeStartJDK15));
    System.out.println("Milis Need JDK 1.8 : " + (afterPrintJDK18 - beforeStartJDK18));

}

请注意,如果减小列表大小,则parallelStream的开销将过大 - 这个事实也称为Amdahl's Law。创建总和的过程与其他循环不同,因此它不是一个好的基准。

有趣的是,每种情况都比在这种情况下慢。