Java8 的 Stream API 性能究竟如何?今天咱们来好好测验一把

做者:Carpenter Lee
java

github.com/CarpenterLee/JavaLambdaInternalsgit

Stream Performance

已经对 Stream API 的用法鼓吹够多了,用起简洁直观,但性能到底怎么样呢?会不会有很高的性能损失?本节咱们对 Stream API 的性能一探究竟。github

为保证测试结果然实可信,咱们将 JVM 运行在 -server模式下,测试数据在 GB 量级,测试机器采用常见的商用服务器,配置以下:数组

OS CentOS 6.7 x86_64
CPU Intel Xeon X5675, 12M Cache 3.06 GHz, 6 Cores 12 Threads
内存 96GB
JDK java version 1.8.0_91, Java HotSpot(TM) 64-Bit Server VM

测试方法和测试数据

性能测试并非容易的事,Java 性能测试更费劲,由于虚拟机对性能的影响很大,JVM 对性能的影响有两方面:服务器

  1. GC 的影响。GC 的行为是 Java 中很很差控制的一块,为增长肯定性,咱们手动指定使用 CMS 收集器,并使用 10GB 固定大小的堆内存。具体到 JVM 参数就是 -XX:+UseConcMarkSweepGC-Xms10G-Xmx10G微信

  2. JIT(Just-In-Time) 即时编译技术。即时编译技术会将热点代码在 JVM 运行的过程当中编译成本地代码,测试时咱们会先对程序预热,触发对测试函数的即时编译。相关的 JVM 参数是 -XX:CompileThreshold=10000app

Stream 并行执行时用到 ForkJoinPool.commonPool()获得的线程池,为控制并行度咱们使用 Linux 的 taskset命令指定 JVM 可用的核数。函数

测试数据由程序随机生成。为防止一次测试带来的抖动,测试 4 次求出平均时间做为运行时间。性能

实验一 基本类型迭代

测试内容:找出整型数组中的最小值。对比 for 循环外部迭代和 Stream API 内部迭代性能。学习

测试程序 IntTest,测试结果以下图:

图中展现的是 for 循环外部迭代耗时为基准的时间比值。分析以下:

  1. 对于基本类型 Stream 串行迭代的性能开销明显高于外部迭代开销(两倍);

  2. Stream 并行迭代的性能比串行迭代和外部迭代都好。

并行迭代性能跟可利用的核数有关,上图中的并行迭代使用了所有 12 个核,为考察使用核数对性能的影响,咱们专门测试了不一样核数下的 Stream 并行迭代效果:

分析,对于基本类型:

  1. 使用 Stream 并行 API 在单核状况下性能不好,比 Stream 串行 API 的性能还差;

  2. 随着使用核数的增长,Stream 并行效果逐渐变好,比使用 for 循环外部迭代的性能还好。

以上两个测试说明,对于基本类型的简单迭代,Stream 串行迭代性能更差,但多核状况下 Stream 迭代时性能较好。

实验二 对象迭代

再来看对象的迭代效果。

测试内容:找出字符串列表中最小的元素(天然顺序),对比 for 循环外部迭代和 Stream API 内部迭代性能。

测试程序 StringTest,测试结果以下图:

结果分析以下:

  1. 对于对象类型 Stream 串行迭代的性能开销仍然高于外部迭代开销(1.5 倍),但差距没有基本类型那么大。

  2. Stream 并行迭代的性能比串行迭代和外部迭代都好。

再来单独考察 Stream 并行迭代效果:

分析,对于对象类型:

  1. 使用 Stream 并行 API 在单核状况下性能比 for 循环外部迭代差;

  2. 随着使用核数的增长,Stream 并行效果逐渐变好,多核带来的效果明显。

以上两个测试说明,对于对象类型的简单迭代,Stream 串行迭代性能更差,但多核状况下 Stream 迭代时性能较好。

实验三 复杂对象归约

从实验1、二的结果来看,Stream 串行执行的效果都比外部迭代差(不少),是否是说明 Stream 真的不行了?先别下结论,咱们再来考察一下更复杂的操做。

测试内容:给定订单列表,统计每一个用户的总交易额。对比使用外部迭代手动实现和 Stream API 之间的性能。

咱们将订单简化为 <userName,price,timeStamp>构成的元组,并用 Order对象来表示。测试程序 ReductionTest,测试结果以下图:

分析,对于复杂的归约操做:

  1. Stream API 的性能广泛好于外部手动迭代,并行 Stream 效果更佳;

再来考察并行度对并行效果的影响,测试结果以下:

分析,对于复杂的归约操做:

  1. 使用 Stream 并行归约在单核状况下性能比串行归约以及手动归约都要差,简单说就是最差的;

  2. 随着使用核数的增长,Stream 并行效果逐渐变好,多核带来的效果明显。

以上两个实验说明,对于复杂的归约操做,Stream 串行归约效果好于手动归约,在多核状况下,并行归约效果更佳。咱们有理由相信,对于其余复杂的操做,Stream API 也能表现出类似的性能表现。

结论

上述三个实验的结果能够总结以下:

  1. 对于简单操做,好比最简单的遍历,Stream 串行 API 性能明显差于显示迭代,但并行的 Stream API 可以发挥多核特性。

  2. 对于复杂操做,Stream 串行 API 性能能够和手动实现的效果匹敌,在并行执行时 Stream API 效果远超手动实现。

因此,若是出于性能考虑,1. 对于简单操做推荐使用外部迭代手动实现,2. 对于复杂操做,推荐使用 Stream API, 3. 在多核状况下,推荐使用并行 Stream API 来发挥多核优点,4. 单核状况下不建议使用并行 Stream API。

若是出于代码简洁性考虑,使用 Stream API 可以写出更短的代码。即便是从性能方面说,尽量的使用 Stream API 也另一个优点,那就是只要 Java Stream 类库作了升级优化,代码不用作任何修改就能享受到升级带来的好处。



- 往期精彩 -

 
     
     
      
      
      
 
     

终于有人把 CountDownLatch,CyclicBarrier,Semaphore 说明白了!


记一次很是有意思的 SQL 优化经历!


来了来了,10个免费后台管理系统模板,接私活专用!


全干货技术公众号Java学习指南

👇👇

长按上方二维码关注公众号

本文分享自微信公众号 - Java学习指南(gh_85b94beaede2)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索