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,一经查实,立即删除!

相关文章

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

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

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

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

solr基本查询和高级查询

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

Angular4 存储访问路由栈信息

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

python基本语法:字典

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

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时则玩家获胜。在这个游戏中为…

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;…

表单隐藏域与display:none

有时候前端进行表单填写是分步骤的&#xff0c;每一步的时候其他步骤相关的表单视图不可见&#xff1b; 针对"不可见"&#xff0c;以下有两种处理方式&#xff1a; ①display&#xff1a;none 这种方式呢&#xff0c;比较简单&#xff0c;就是将三个步骤分3个div&…

视频领域的Instagram:Viddy用户突破2600万

北京时间5月9日消息&#xff0c;据TheNextWeb报道&#xff0c;视频分享应用Viddy的注册用户数量已经达到2600万&#xff0c;而上个月的用户数量还是650万。日均增长用户超过50万&#xff0c;成绩斐然&#xff0c;投资者对Viddy目前的增长表示很满意。 Viddy是如何达到这样的成…

2018年7月份,python上传自己的包库到pypi官网的方法

最近pypi官网进行了更新&#xff0c;老的上传网址作废了。记录下上传到pypi的方法 0、去pypi官网注册账号&#xff0c;没账号是不可能上传的&#xff0c;想想也是那不乱套了吗&#xff0c;注册后会收到一个邮件需要点击然后重新登录 1、目录就是这样 &#xff0c;我要上传muli…

写给大数据开发初学者的话2

见 : http://lxw1234.com/archives/2016/11/782.htm 如果你已经按照《写给大数据开发初学者的话》中第一章和第二章的流程认真完整的走了一遍&#xff0c;那么你应该已经具备以下技能和知识点&#xff1a; 0和Hadoop2.0的区别&#xff1b;MapReduce的原理&#xff08;还是那个…

Pandas的结构和应用

Pandas处理以下三个数据结构 - 系列(Series)----一维ndarray   特点&#xff1a;带有标签&#xff0c;可以使用标签作为索引&#xff0c;大小不能改变&#xff0c;内部数据可以改变。 属性&#xff1a;与NumPy类似&#xff0c;多了一个轴标签axis lables 数据…

时间即财富:创业者浪费精力的八个错误

导读&#xff1a;本文作者Jeff Miller是美食网页应用Punchfork的创始人&#xff0c;同时也是DuckDuckGo、Ginzametrics、Art.sy、DataMinr以及Forkly的投资人。作者通过对自己创业初期一些错误选择进行盘点&#xff0c;告诉读者在创业初期应该学会选择&#xff0c;因为在创业初…

写给大数据开发初学者的话3

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 如果你已经按照《写给大数据开发初学者的话2》中第三章和第四章的流程认真完整的走了一遍&#xff0c;那么你应该已经具备以下技能和知识…

十五周二次课

18.6 负载均衡集群介绍 主流开源软件LVS、keepalived、haproxy、nginx等其中LVS属于4层&#xff08;网络OSI 7层模型&#xff09;&#xff0c;nginx属于7层&#xff0c;haproxy既可以认为是4层&#xff0c;也可以当做7层使用keepalived的负载均衡功能其实就是lvslvs这种4层的负…

写给大数据开发初学者的话4

见&#xff1a;http://lxw1234.com/archives/2016/11/795.htm 如果你已经按照《写给大数据开发初学者的话3》中第五章和第六章的流程认真完整的走了一遍&#xff0c;那么你应该已经具备以下技能和知识点&#xff1a; 为什么Spark比MapReduce快。使用SparkSQL代替Hive&#xff…

域名解析服务之DNS查询类型

在实际应用中DNS查询主要分为两种方式查询&#xff1a;1.递归查询&#xff1b;2.迭代查询 一般情况下&#xff1a;为了减少资源的消耗&#xff0c;网络中客户端与所属的本地DNS服务器查询方式通常为递归查询&#xff0c;本地DNS服务器与外部的公共DNS服务器间的查询方式为迭代查…

MFC Ribbon界面设计

Ribbon是类似于office2007样式的界面&#xff0c;它替代了传统的MFC程序里的菜单和工具栏 MFC默认生成的Ribbon功能少&#xff0c;需要我们自己添加一些控件和图片等元素使界面好看 看下面的一个界面&#xff0c;是VC2010示例里的 看到它与默认Ribbon样式的区别&#xff1a; 工…