Java 8中用法优雅的Stream,性能也"优雅"吗?
之前的文章中我们介绍了Java 8中Stream相关的API,我们提到Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
那么,Stream API的性能到底如何呢,代码整洁的背后是否意味着性能的损耗呢?本文我们对Stream API的性能一探究竟。
为保证测试结果真实可信,我们将JVM运行在-server模式下,测试数据在GB量级,测试机器采用常见的商用服务器,配置如下:
一、测试方法与数据
性能测试并不是容易的事,Java性能测试更费劲,因为虚拟机对性能的影响很大,JVM对性能的影响有两方面:
GC的影响。GC的行为是Java中很不好控制的一块,为增加确定性,我们手动指定使用CMS收集器,并使用10GB固定大小的堆内存。具体到JVM参数就是-XX:+UseConcMarkSweepGC -Xms10G -Xmx10G
JIT(Just-In-Time)即时编译技术。即时编译技术会将热点代码在JVM运行的过程中编译成本地代码,测试时我们会先对程序预热,触发对测试函数的即时编译。相关的JVM参数是-XX:CompileThreshold=10000。
Stream并行执行时用到ForkJoinPool.commonPool()得到的线程池,为控制并行度我们使用Linux的taskset命令指定JVM可用的核数。
测试数据由程序随机生成。为防止一次测试带来的抖动,测试4次求出平均时间作为运行时间。
二、基本类型迭代
测试内容:找出整型数组中的最小值。对比for循环外部迭代和Stream API内部迭代性能。
测试程序IntTest,测试结果如下图:
图中展示的是for循环外部迭代耗时为基准的时间比值。分析如下:
对于基本类型Stream串行迭代的性能开销明显高于外部迭代开销(两倍);
Stream并行迭代的性能比串行迭代和外部迭代都好。
并行迭代性能跟可利用的核数有关,上图中的并行迭代使用了全部12个核,为考察使用核数对性能的影响,我们专门测试了不同核数下的Stream并行迭代效果:
分析,对于基本类型:
使用Stream并行API在单核情况下性能很差,比Stream串行API的性能还差;
随着使用核数的增加,Stream并行效果逐渐变好,比使用for循环外部迭代的性能还好。
以上两个测试说明,对于基本类型的简单迭代,Stream串行迭代性能更差,但多核情况下Stream迭代时性能较好。
三、对象迭代
接下来我们再来看对象的迭代效果。
测试内容:找出字符串列表中最小的元素(自然顺序),对比for循环外部迭代和Stream API内部迭代性能。
测试程序StringTest,测试结果如下图:
结果分析如下:
对于对象类型Stream串行迭代的性能开销仍然高于外部迭代开销(1.5倍),但差距没有基本类型那么大。
Stream并行迭代的性能比串行迭代和外部迭代都好。
再来单独考察Stream并行迭代效果:
分析,对于对象类型:
使用Stream并行API在单核情况下性能比for循环外部迭代差;
随着使用核数的增加,Stream并行效果逐渐变好,多核带来的效果明显。
以上两个测试说明,对于对象类型的简单迭代,Stream串行迭代性能更差,但多核情况下Stream迭代时性能较好。
四、复杂对象归约
从实验一、二的结果来看,Stream串行执行的效果都比外部迭代差(很多),是不是说明Stream真的不行了?先别下结论,我们再来考察一下更复杂的操作。
测试内容:给定订单列表,统计每个用户的总交易额。对比使用外部迭代手动实现和Stream API之间的性能。
我们将订单简化为<userName, price, timeStamp>构成的元组,并用Order对象来表示。测试程序ReductionTest,测试结果如下图:
Stream API的性能普遍好于外部手动迭代,并行Stream效果更佳;
再来考察并行度对并行效果的影响,测试结果如下:
分析,对于复杂的归约操作:
使用Stream并行归约在单核情况下性能比串行归约以及手动归约都要差,简单说就是最差的;
随着使用核数的增加,Stream并行效果逐渐变好,多核带来的效果明显。
以上两个实验说明,对于复杂的归约操作,Stream串行归约效果好于手动归约,在多核情况下,并行归约效果更佳。我们有理由相信,对于其他复杂的操作,Stream API也能表现出相似的性能表现。
五、结论
上述三个实验的结果可以总结如下:
对于简单操作,比如最简单的遍历,Stream串行API性能明显差于显示迭代,但并行的Stream API能够发挥多核特性。
对于复杂操作,Stream串行API性能可以和手动实现的效果匹敌,在并行执行时Stream API效果远超手动实现。
所以,如果出于性能考虑,
1. 对于简单操作推荐使用外部迭代手动实现
2. 对于复杂操作,推荐使用Stream API,
3. 在多核情况下,推荐使用并行Stream API来发挥多核优势
4.单核情况下不建议使用并行Stream API
如果出于代码简洁性考虑,使用Stream API能够写出更短的代码。即使是从性能方面说,尽可能的使用Stream API也另外一个优势,那就是只要Java Stream类库做了升级优化,代码不用做任何修改就能享受到升级带来的好处。
Java 8中用法优雅的Stream,性能也"优雅"吗?的更多相关文章
- java 8中构建无限的stream
目录 简介 基本使用 自定义类型 总结 java 8中构建无限的stream 简介 在java中,我们可以将特定的集合转换成为stream,那么在有些情况下,比如测试环境中,我们需要构造一定数量元素的 ...
- Java 8 中有趣的操作 Stream
Stream 不是java io中的stream 对象创建 我们没有必要使用一个迭代来创建对象,直接使用流就可以 String[] strs = {"haha","hoh ...
- Java 8中处理集合的优雅姿势——Stream
在Java中,集合和数组是我们经常会用到的数据结构,需要经常对他们做增.删.改.查.聚合.统计.过滤等操作.相比之下,关系型数据库中也同样有这些操作,但是在Java 8之前,集合和数组的处理并不是很便 ...
- Java 8 中如何优雅的处理集合
Java 8 中如何优雅的处理集合(Stream API) 在Java中,集合和数组是我们经常会用到的数据结构,需要经常对他们做增.删.改.查.聚合.统计.过滤等操作.相比之下,关系型数据库中也同样有 ...
- Java开发中程序和代码性能优化
现在计算机的处理性能越来越好,加上JDK升级对一些代码的优化,在代码层针对一些细节进行调整可能看不到性能的明显提升, 但是我觉得在开发中注意这些,更多的是可以保持一种性能优先的意识,对一些敲代码时间比 ...
- Java 16 中新增的 Stream 接口的一些思考
这里先提一个题外话,如果想看 JDK 不同版本之间有何差异,增加或者删除了哪些 API,可以通过下面这个链接查看: https://javaalmanac.io/jdk/17/apidiff/11/ ...
- 使用Java 8中的Stream
Stream是Java 8 提供的高效操作集合类(Collection)数据的API. 1. 从Iterator到Stream 有一个字符串的list,要统计其中长度大于7的字符串的数量,用迭代来实现 ...
- 【Java学习笔记之二十】final关键字在Java继承中的用法小结
谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. ...
- 详解Java 8中Stream类型的“懒”加载
在进入正题之前,我们需要先引入Java 8中Stream类型的两个很重要的操作: 中间和终结操作(Intermediate and Terminal Operation) Stream类型有两种类型的 ...
随机推荐
- 使用 Vscode +PlantUml 画uml图
什么是PlantUML PlantUML是一个快速创建UML图形的组件,官网上之所以称它是一个组件,主要是因为多数情况下我们都是在Eclipse.NetBenas.Intellijidea. Emac ...
- C# DataConstruct 数据结构关于 Array,ArrayList,List,HashTable,Dictionnary的学习记录
Array: 数组,开辟连续存储的内存存储数据.Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据 优点: 1.查询速度快,可以利用索引快速查询到对 ...
- pyqt win32发送QQ消息
标题应该改为:python+win32发送QQ消息,全程使用python套个pyqt壳. 其实代码来自: http://blog.csdn.net/suzyu12345/article/details ...
- HTTP/1.1 chunked 解码
0.简介 1.定义 RFC定义 https://tools.ietf.org/html/rfc2616#section-3.6.1 Chunked-Body = *chunk last-chunk t ...
- Delphi 10.3版本获取windows系统版本和CPU信息
procedure TForm1.Button1Click(Sender: TObject); var mm:TRegistry; cpu:string; begin mm:=TRegistry.Cr ...
- XYZZY spfa 最长路 判环
题意: 有n个点 m条边 每个边有权值 一开始有一百血 每次经过一条路都会加上其权值 判断是否能够到达n 显然 有正环的时候肯定能够到达 最短路好题!!!!!!! 显用folyed判断是否联通 ...
- js中将字符串作为函数名来调用的方法
方法:eval() eg: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...
- Django合集
Django基础 Django--简介 Django--web框架简介 浅析uWSGI.uwsgi.wsgi Django--url(路由)配置 Django--模板层 Django--视图层 Dja ...
- AGC001F - Wide Swap
Description 给你一个长度为$n$的排列,每次可以交换$|i-j|\geq K$并且$|a_i-a_j|=1$的数对,问你经过若干次变换后最小字典序的排列是啥 Solution 对$a$做一 ...
- leetcode-只出现一次的数字
题目:只出现一次的数字 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? ...