Performance Java8,列表的第一次处理比后续处理慢
为了检查Java8流和lambda的性能,我正在运行一些测试(非常基本,没有什么特别之处)。使用一个1000万POJO的Performance Java8,列表的第一次处理比后续处理慢,performance,java-8,java-stream,Performance,Java 8,Java Stream,为了检查Java8流和lambda的性能,我正在运行一些测试(非常基本,没有什么特别之处)。使用一个1000万POJO的ArrayList,我只想得到BigDecimal字段的平均值。为了获取多个样本,我运行了五次该过程,令我惊讶的是,这五次运行中的第一次运行速度比其他运行速度要慢得多。我第一次得到的值是0.38秒,其他四次得到的值是0.04秒。这是10倍的速度!!!我还使用老式的for(pojop:pojos)做了同样的测试,结果相似。为什么会发生这种情况,我如何利用它?我使用的代码是: fo
ArrayList
,我只想得到BigDecimal
字段的平均值。为了获取多个样本,我运行了五次该过程,令我惊讶的是,这五次运行中的第一次运行速度比其他运行速度要慢得多。我第一次得到的值是0.38秒,其他四次得到的值是0.04秒。这是10倍的速度!!!我还使用老式的for(pojop:pojos)
做了同样的测试,结果相似。为什么会发生这种情况,我如何利用它?我使用的代码是:
for (int i = 0; i < 5; i++) {
long init = System.nanoTime();
BigDecimal sum = lista.parallelStream().map(x -> x.getCosto()).reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal avg = sum.divide(BigDecimal.valueOf(registros));
long end = System.nanoTime();
System.out.println("End of processing: " + avg + " in "
+ ((end - init) / 1000000000.0) + " seconds.");
}
for(int i=0;i<5;i++){
long init=System.nanoTime();
BigDecimal sum=lista.parallelStream().map(x->x.getCosto()).reduce(BigDecimal.ZERO,BigDecimal::add);
BigDecimal平均值=总和除法(BigDecimal.valueOf(registros));
long end=System.nanoTime();
System.out.println(“处理结束:+avg+“in”
+((end-init)/100000000.0)+“秒”;
}
当您第一次调用流API时,初始化流API需要一个恒定的延迟,包括以下步骤:
- 从
包加载许多助手类java.util.stream
- 从
包加载lambda生成类(如java.lang.invoke
)LambdaMetafactory
- 为流管道中涉及的lambda和方法引用生成运行时表示(包括流API中内部使用的lambda)
- 所有这些字节码的分层编译(解释器->C1 JIT->C2 JIT)。C2 JIT编译(生成最快代码)仅在特定数量的方法调用(如5000)或特定数量的后缘(如果方法内部有大循环,则循环迭代;如40000)后触发。当大多数代码未经C2编译时,其运行速度会慢得多。此外,JIT编译器线程需要一些CPU时间,这些时间可能用于实际计算
- 对于并行流:初始化公共
,创建新线程ForkJoinPool
在您的特定情况下,您正在密集地使用堆,因此堆的扩大也可能是额外缓慢的原因。如果您的
-Xms
默认值太小,则垃圾收集器将执行几个完整的gc循环,直到将堆放大到合适的大小。您可以使用Xms==Xmx(例如-Xmx1G-Xms1G
)运行测试,这可能会提高第一次迭代的速度。不幸的是,进行性能测试并不是那么容易,特别是在与lambda表达式和方法引用相结合时。你需要使用适当的工具,比如JMH框架。我不同意重复标记。OP询问为什么第一次处理较慢。事实确实如此。即使OP使用JMH重写基准测试,第一次迭代也会比后续迭代慢得多。考虑到我们所说的是几十毫秒,而不是微秒或纳秒,OPs测量在方法上并不是那么糟糕。也许值得为问题的“我如何利用它?”部分添加一个一般答案:一般来说,避免代码重复,创建可重用类,等等。