Java 8 Stream的性能到底如何?

Java 8提供的流的基于Lambda表达式的函数式的操作写法让人感觉很爽,笔者也一直用的很开心,直到看到了Java8 Lambda表达式和流操作如何让你的代码变慢5倍,笔者当时是震惊的,我读书少,你不要骗我。瞬间我似乎为我的Server Application速度慢找到了一个很好地锅,不过这个跟书上讲的不一样啊。于是笔者追本溯源,最后找到了始作俑者自己的分析:原文

不久之前我在社区内发表了这篇文章: I mused about the performance of Java 8 streams ,上面的测试结果貌似很有道理。其中一个测试是将传统的for-循环与Stream进行了比较。很多人表示了震惊、不相信等等很多很多的情绪,甚至有人直接说Stream是个什么鬼,哪凉快哪呆着去。这是没有道理的,毕竟不能通过一个简单地只是一个环境下的测试就否定这些。

在之前的测评中,在500,000个随机的整形数的数组的遍历中,我们得出的结论是for-循环的速度会比Stream的速度快上15倍。其中for-循环的数组如下所示:

int[] a = ints;
int e = ints.length;
int m = Integer.MIN_VALUE;for (int i = 0; i < e; i++)if (a[i] > m) m = a[i];

同样的,我们建立了一个原始类型的IntStream

int m = Arrays.stream(ints).reduce(Integer.MIN_VALUE, Math::max);

在我们这个过时的设备上(双核)跑出来的结果是:

int-array, for-loop : 0.36 ms
int-array, seq. stream: 5.35 ms

for循环的方式明显的比Stream流要快很多很多,然后我们选择了另一台4核的设备,发现这个比例因子变成了4.2(原来是15)。这个结果的详细信息可以看Nicolai Parlog’s blog 这个文章:

正如我们所料,不同的环境可能会引发不同的结果。不过我们评测的核心:for-循环速度奏是比Stream快,在不同的平台上是一致的。

接下来,我们不再测试原始类型,改用了ArrayList<Integer>,同样是填充了500000个随机数,然后结果是:

ArrayList, for-loop   : 6.55 ms
ArrayList, seq. stream: 8.33 ms

是的,for-循环的速度确实还是会快一点,但是很明显这种差距缩小了。这个结果并不令人惊讶,实际上整个测试的性能主要取决于内存访问与遍历这两大块。其中内存访问这个还受限制于硬件本身,所以不同的平台上会有不同的结果。实际上在我们的测试中出现这样的结果并不会令人惊讶,毕竟我们特意选择了一个比较极端的情况,代表了范围内的某个极端,可以解释如下:

  • 我们将for-loops与Streams进行了比较。循环本身是JIT友好的。编译器本身有了40年以上的经验,然后我们选择了循环这个JIT编译器重点优化的部分。这是所谓的某个极端:一个JIT友好的,高度优化的访问序列元素的方法。而如果是使用流的话也就意味着会在主框架内进行调用,不可避免地增加内存调用。而一个JIT编译器本身是有一个上限的,虽然大部分情况下是用不满的。因此,我们将这种情况分为JIT友好与不友好,而for-循环本身是处于JIT友好的这一边,因此它自然能够赢得这个测试,并没有神马奇怪。

  • 我们将原始类型的序列与引用类型的序列进行了比较。这两种情况可以用缓存友好/不友好来区分。一个原始类型int的序列是非常缓存友好的,特别是当未来Java引入不可变序列的时候。而一个引用类型的序列,即使用了基于数组的,就像ArrayList的这样的存储,也是只有很小的概率进行很好地缓存。每次独立地对于序列成员的访问需要获取指针指向的地址然后获取其内容,也就意味着缓存的失效。很明显地,一个使用了int[]for-循环肯定处在缓存友好这一边,自然与序列引用的Stream相比性能上要好上很多。

  • 我们将元素轻量级使用与CPU密集型使用相比。更重要的是,我们将这种两个两个的比较寻找最大值的计算与Taylor相似度下寻找正弦值的计算进行了比较。在下面一个实验中,我们会以相对而言复杂一点的CPU密集型的运算为例,可能获取到这个值需要一分钟的时间。我们将此称作CPU友好或者CPU不友好的分割。一般来说,对于序列中的元素进行重量级的CPU密集型的运算的时候,也就是所谓的CPU不友好运算时,评测结果往往由CPU的运算速度决定,而对于上面讲的缓存缺失以及JIT循环的优化就变得不那么重要了。

讲了这么多,我们已经可以发现上面评测中对于int[]类型的数组中寻找最大值的这件事是受到JIT友好以及缓存友好这两个因素决定的。这种情况下,当然for-循环会占了很大优势,如果没做到这样才会让人惊讶呢。那么如果我们对于上文中所讲的CPU密集型的情况,这也是一种极端情况,进行评测,其中for-循环式这样的:

int[] a = ints;
int e = a.length;
double m = Double.MIN_VALUE;for (int i = 0; i < e; i++) {double d = Sine.slowSin(a[i]);if (d > m) m = d;
}

Stream的用法如下:

Arrays.stream(ints).mapToDouble(Sine::slowSin).reduce(Double.MIN_VALUE, (i, j) -> Math.max(i, j));

最后的结果是:

for``-loop   : ``11.82` `ms
seq. stream: ``12.15` `ms

这个评测依旧是在上文说到的那个老旧的机器上进行的。确实for-循环的效率是比Stream要快的,不过可以看得出来这种差距不再明显了。换种说法,这种差距在统计评测的角度来看还是很重要的,不过在实际的应用过程中已经无足轻重了。到这里我们证明了与上次评测相悖的一个观点:其实Stream与for-循环之间的性能并木有很大的差异。

最后来总结一波,在有些情况下,Stream的效率确实会比for-循环要慢上很多倍,然后在其他大部分情况下是没有虾米差异的。你可以觉得Stream很酷然后就去使用它,或者为了优化你的应用的性能而依旧选择旧的语法。同时,也不要无缘无故就觉得人家Stream损害了你应用的性能,那是你自己用得不好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/451349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【linux】使用swap文件恢复非正常关闭的文件

前言 使用vim的时候&#xff0c;文件编辑过程中可能会出现bug&#xff0c;导致非正常关闭。为了保存刚刚修改的内容&#xff0c;需要对文件进行恢复。 操作过程 1.查看目录文件 zrjzrj-ThinkPad-E470:~/work/code/facedetection/src$ ll total 48 drwxrwxr-x 2 xxx xxx 4096 S…

js中比较时间字串大小

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 var start Date.parse($("#str_atBeginDate").val().replace("-","/"));var end Date.parse($("#…

MySQL运维系列 之 如何监控大事务

long transaction背景 大家有没有遇到这样的情况 某个SQL执行特别慢&#xff0c;导致整个transaction一直处于running阶段某个Session的SQL已经执行完了&#xff0c;但是迟迟没有commit&#xff0c;一直处于sleep阶段某个Session处于lock wait阶段&#xff0c;迟迟没有结束以上…

再见腾讯,创业我来了!!!

发现好久没有更新博客了&#xff0c;确实自从进了腾讯以后博客写的寥寥无几&#xff0c;一来是忙&#xff0c;而来是写博客没以前那么有成就感了。但是今天在半夜我又鬼使神差的想写了&#xff0c;因为尼玛欧冠巴萨 VS AC米兰还有将近一个小时&#xff0c;哥实在是等的蛋疼。有…

“docker-app”实用工具分享,大大提高 Compose 文件复用率

本文首发自“Docker公司”公众号&#xff08;ID&#xff1a;docker-cn&#xff09;编译丨小东每周一、三、五 与您不见不散&#xff01; Docker Compose 在开发人员中非常流行&#xff0c;它用来描述应用程序。目前&#xff0c;GitHub 上有超过30万个 Docker Compose 文件。通过…

9.11学习笔记

备注&#xff1a; <span class"kp"> <a href""></a></san>a 是特殊的 要改变a里面的颜色&#xff0c;必须直接给a设置&#xff0c;给a的父级设置不行 属性继承&#xff1a;明明是父级上设置样式&#xff0c;结果后代标签也跟着发生…

bootstrap-validator 验证一个标签同时验证另一个指定标签

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 如图 验证 str_atBeginDate 同时把 str_atTermDate 也作一次验证&#xff1a; 注意 红框中 " value “ 不能少&#xff0c;我之…

solr基本查询和高级查询

查询参数常用&#xff1a; q - 查询字符串&#xff0c;必须的。fl - 指定返回那些字段内容&#xff0c;用逗号或空格分隔多个。start - 返回第一条记录在完整找到结果中的偏移位置&#xff0c;0开始&#xff0c;一般分页用。rows - 指定返回结果最多有多少条记录&#xff0c;配…

送给“苦逼”的IT人系列

送给“苦逼”的IT人系列1&#xff1a;IT人的“钱”景以及收入的两道坎 虽然IT工作五花八门&#xff0c;而且年纪有老有少&#xff0c;但IT人的收入总体还是比较有规律的&#xff0c;很明显的可以看出有两道坎&#xff0c;分别是10W&#xff0c;和30W&#xff0c;当然&#xff…

锐动SDK应用于行车记录仪

方案架构手机端直播与录播功能忠实记录旅途中各种突发事件&#xff0c;还原事实真相&#xff0c;与家人和朋友分享沿途美景&#xff0c;一同感受美妙之旅。强大的视频编辑功能&#xff0c;像编辑图片一样给视频添加各种滤镜&#xff0c;配音&#xff0c;配乐&#xff0c;标题文…

Angular4 存储访问路由栈信息

一、实现方法1.可以通过路由守卫&#xff0c;可以给父级路由添加&#xff0c;若无父级路由&#xff0c;则需要每个路由都需要添加守卫&#xff0c;即每个页面都需要调该方法例如&#xff1a;jdb-app端上的tool.service.ts是每个页面都会调取的方法&#xff0c;可以将监听路由函…

python基本语法:字典

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 一、数据类型和对应符号&#xff1a; 元组 ( ) 列表 [ ] 字典 { } 二、字典是python中唯一的映射类型&#xff08;哈希表&#xf…

网址URL中特殊字符转义编码

字符 - URL编码值 空格 - %20" - %22# - %23% - %25& - %26( - %28) - %29 - %2B, - %2C/ - %2F: - %3A; - %3B< - %3C - %3D> - %3E? - %3F - %40\ - %5C| - %7C…

无病呻吟系列

无病呻吟1&#xff1a;给应届生的话 讲到第一份工作&#xff0c;自然要给应届毕业生一点建议。 其实这不是我的初衷&#xff0c;写这篇文章的主要目的&#xff0c;是给那些工作了3,5年&#xff0c;正进入迷茫期或已经进入迷茫期的人看的。至于应届毕业生&#xff0c;我想&…

JavaScript实现向OL列表内动态添加LI元素的方法

2019独角兽企业重金招聘Python工程师标准>>> <script type"text/javascript"> function addItem() {var myitem document.getElementById("ItemToAdd").value;var mylistItems document.getElementById("mylist");var newP …

【blockly教程】第五章 循环结构

在这里&#xff0c;我们将介绍一个新游戏--Pond Tutor 在Pond Tutor(https://blockly-games.appspot.com/pond-tutor)这个游戏中&#xff0c;我们将扮演黄色的鸭子&#xff0c;通过不断的发炮弹去攻击红色的鸭子&#xff0c;当红色的鸭子血条减为0时则玩家获胜。在这个游戏中为…

数据的PB级别是什么?

PB是数据存储容量的单位&#xff0c;它等于2的50次方个字节&#xff0c;或者在数值上大约等于1000个TB。”一提到数据量级&#xff0c;人们通常会联想到美国国会图书馆&#xff0c;德勤、麦肯锡、IBM、Gartner和移动广告公司。Adfonic的数据专家向TechTarget记者介绍了PB级数据…

js 获取字符串最后一位的4种方法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 方法一&#xff1a;运用String对象下的charAt方法 charAt() 方法可返回指定位置的字符。 复制代码代码如下:str.charAt(str.length – …

360董事长周鸿祎跨足手机市场是福还是祸?

编者按&#xff1a;奇虎360董事长周鸿祎做的这个“思考了半年”的决定&#xff1a;进军智能手机&#xff0c;是于5月4日夜10时45分。他所发布新浪微博称&#xff1a;“现在每个人都想拥有高性能的智能手机&#xff0c;高富帅白富美人手一iPhone&#xff0c;难道吊丝只能买便宜低…

3 .6 .5 优化Ad-Hoc工作负载

执行计划生成后会存储在plan cache中&#xff0c;以便重用&#xff0c;如果计划缓存从来都没有被重用 过&#xff0c;将会造成内存资源的浪费&#xff0c;这有可能是由于非参数化的Ad-hoc (即席查询&#xff09;引起的。 当执行代码时&#xff0c;会产生一个hash值&#xff0c;…