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

为保证测试结果真实可信,我们将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=10000

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迭代时性能较好。

实验三 复杂对象归约

从实验一、二的结果来看,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类库做了升级优化,代码不用做任何修改就能享受到升级带来的好处。

本文链接

Java Stream API性能测试的更多相关文章

  1. 恕我直言你可能真的不会java第2篇:Java Stream API?

    一.什么是Java Stream API? Java Stream函数式编程接口最初是在Java 8中引入的,并且与lambda一起成为Java开发的里程碑式的功能特性,它极大的方便了开放人员处理集合 ...

  2. Java Stream API入门篇

    本文github地址 你可能还没意识到Java对函数式编程的重视程度,看看Java 8加入函数式编程扩充多少类就清楚了.Java 8之所以费这么大功夫引入函数式编程,原因有二: 代码简洁,函数式编程写 ...

  3. Java Stream API进阶篇

    本文github地址 上一节介绍了部分Stream常见接口方法,理解起来并不困难,但Stream的用法不止于此,本节我们将仍然以Stream为例,介绍流的规约操作. 规约操作(reduction op ...

  4. java stream Api

    Stream的简单使用 Stream的使用分为两种类型: Intermediate,一个Stream可以调用0到多个Intermediate类型操作,每次调用会对Stream做一定的处理,返回一个新的 ...

  5. [java]Stream API——collect、reduce、orElse(x)

    一.collect 1.R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) supplier:一个能创造 ...

  6. [java]Stream API——map和flatMap的区别

    map方法 map的含义为映射.是指对于Stream中包含的每一个元素使用给定的转换函数进行转换. map传入的Lambda表达式必须是Function实例,参数可以为任意类型,而其返回值也是任性类型 ...

  7. JDK 8 新特性之函数式编程 → Stream API

    开心一刻 今天和朋友们去K歌,看着这群年轻人一个个唱的贼嗨,不禁感慨道:年轻真好啊! 想到自己年轻的时候,那也是拿着麦克风不放的人 现在的我没那激情了,只喜欢坐在角落里,默默的听着他们唱,就连旁边的妹 ...

  8. Java 函数式编程(Lambda表达式)与Stream API

    1 函数式编程 函数式编程(Functional Programming)是编程范式的一种.最常见的编程范式是命令式编程(Impera Programming),比如面向过程.面向对象编程都属于命令式 ...

  9. Java 8 Stream API Example Tutorial

    Stream API Overview Before we look into Java 8 Stream API Examples, let’s see why it was required. S ...

随机推荐

  1. Codeforce 水题报告(2)

    又水了一发Codeforce ,这次继续发发题解顺便给自己PKUSC攒攒人品吧 CodeForces 438C:The Child and Polygon: 描述:给出一个多边形,求三角剖分的方案数( ...

  2. Linux内存中的 buffer 和 cache 到底是个什么东东?

    Linux 中的 free 命令,会输出: total 总量 used  已使用 free 空闲 shared 共享内存 buffers cached 前面四项都比较好理解,一看我也就知道啥意思了.但 ...

  3. HTML确认密码

    html确认密码   今天准备分享一个小知识点,就是确认登录界面 <body ><form>输入户名: <input type="text" name ...

  4. Fraction to Recurring Decimal leetcode

    Given two integers representing the numerator and denominator of a fraction, return the fraction in ...

  5. ACM 比大小

    比大小 时间限制:3000 ms  |  内存限制:65535 KB 难度:2   描述 给你两个很大的数,你能不能判断出他们两个数的大小呢? 比如123456789123456789要大于-1234 ...

  6. 九度oj1163题

    题目描述: 输入一个整数n(2<=n<=10000),要求输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数,如果没有则输出-1. 输入: 输入有多组数据. 每组一行,输入n. ...

  7. [读书系列] 深度探索C++对象模型 初读

    2012年底-2014年初这段时间主要用C++做手游开发,时隔3年,重新拿起<深度探索C++对象模型>这本书,感觉生疏了很多,如果按前阵子的生疏度来说,现在不借助Visual Studio ...

  8. yaourt 之 Curl 错误

    最近执行 yaourt 更新时总是出现以下错误: curl error: Couldn't connect to server 无法进行更新.把配置中的下载工具更换了成 axel 等其它下载工具,还是 ...

  9. XML(20161113)

    XML: 可扩展标记语言 在页面之间传递数据使用的,专门为了数据传输而存在 载体 HTML:超文本标记语言 显示 XML写法: 标签:<标签名></标签名> 特点:1.必须要有 ...

  10. 关于<context:property-placeholder>的一个有趣现象

    转:http://stamen.iteye.com/blog/1926166 先来看下A和B两个模块 A模块和B模块都分别拥有自己的Spring XML配置,并分别拥有自己的配置文件: A模块 A模块 ...