【翻译】Java 8 中的并行流处理 -- 顺序流处理与并行流处理的性能对比

本贴最后更新于 973 天前,其中的信息可能已经事过境迁

Java 8 中的并行流处理--顺序流处理与并行流处理的性能对比

出处:https://blog.oio.de/2016/01/22/parallel-stream-processing-in-java-8-performance-of-sequential-vs-parallel-stream-processing

并行处理在当今社会无处不在。由于 cpu 核心数量的增加和硬件成本的降低,使得集群系统更加便宜,并行处理似乎是下一个 big thing。

Java 8 通过新的流 API 和在集合和数组上创建并行处理的简化来关注这一事实。让我们来看看这是如何工作的。

假设 myList 是一个整数列表,包含 500.000 个整数值。在前 java 8 时代,对这些整数值进行汇总的方法是使用 for each 循环。

for (int i :myList)
result+=i;

从 java 8 开始,我们可以用流来做同样的事情

myList.stream().sum();

并行化处理非常容易,我们只需用关键字 parallelStream() 来代替 stream,或者如果我们还有一个 stream,就用 parallel()
image.png

所以

并行化流操作代码应该是这个样子的

myList.parallelStream().sum()

这样的改写很容易将计算分散到线程和可用的 cpu 核心上。但我们知道,多线程和并行处理的开销是昂贵的。问题是什么时候使用并行流,什么时候使用串行流会更有利于性能。

首先让我们看一下幕后发生了什么。并行流使用 Fork/Join 框架进行处理。这意味着 stream-sourceforked(也就是被拆分),并交给 fork/join-poolworkers 执行。

但在这里我们发现需要思考的第一点,并不是所有的 stream-source 被都可以像其他流源一样被分割。想想 ArrayList,它的内部数据表示是基于一个数组的。拆分这样的 stream 很容易,因为可以计算中间元素的索引并拆分数组。

如果我们有一个 LinkedList,那么分割数据元素就会更加复杂。实现者必须从第一个元素开始浏览所有的元素,找到可以进行分割的元素。因此,例如 LinkedLists 对于并行流来说表现得很糟糕。

image.png

这是我们可以保留的第一个关于平行流性能的事实。

S - 源集合(collection)必须是可有效分割的

分割一个集合,管理 fork 和 join 任务,对象创建和垃圾收集也是一种算法上的开销。只有当需要在 cpu 核上完成的工作非同小可和/或集合足够大时,这才是值得的。当然,我们也有很多 cpu 核。

一个错误的例子是计算 5 个整数值的最大值。

IntStream.rangeClosed(1, 5).reduce( Math::max).getAsInt();
这里为 fork/join 准备和处理数据的开销是如此之大,以至于这里的串行流要快得多。Math.max 函数在这里的 CPU 成本不是很高,而且我们的数据元素较少

但是,当每个元素执行的函数更复杂时,它就越来越有价值了--确切地说,是 "更密集的 cpu"。例如,计算每个元素的正弦值而不是最大值。

当对国际象棋游戏进行编程时,每一步棋的评估也是这样的例子。许多评估可以并行进行。而且我们有大量可能的下一步棋。

这对并行处理来说是完美的。

而这是我们可以保留的第二个关于平行流性能的事实。

NQ - "元素数量每个元素的成本 "的系数应该很大

但这也意味着反过来说,当每个元素的操作成本较高时,集合可以更小。

或者当每个元素的操作不是那么密集的时候,我们需要一个有很多元素的大集合,这样并行流的使用才会有回报。

这直接取决于我们可以保留的第三个事实

C - CPU 核心的数量 - 越多越好 > 1 个是必须的

在单核机器上,由于管理开销的原因,并行流的表现总是比串行流差。就像公司有很多项目负责人而只有一个人在做工作一样。

越多越好--不幸的是,在现实世界中,这并不是在所有情况下都是正确的,例如,当集合太小,CPU 核心启动时--也许是从 energy safe mode--才发现没有什么可做的。

为了确定是否使用平行流,对每个元素的函数也有要求。这与其说是性能问题,不如说是并行流是否能如期工作的问题。

该功能必须是...

  • ...独立,这意味着每个元素的计算不能依赖或影响任何其他元素的计算。
  • ...无干扰,这意味着函数在处理时不会修改基础数据源。
  • ...无状态。

这里我们有一个在并行流中使用的有状态 lambda 函数的例子。这个例子取自 java JDK API,显示了一个简化的 distinct()实现。

Set seen = Collections.synchronizedSet(new HashSet());
stream.parallel().map(e -> { if (seen.add(e)) return 0; else return e; })...

因此,这让我们看到了我们可以保留的第四个事实。

F - 每个元素的函数必须是独立的

总结一下

image.png

还有其他一些情况下,我们不应该将我们的流并行化吗?是的,有的。

总是要考虑你的每个元素函数在做什么,以及这是否适合于并行处理的世界。当你的函数正在调用一些同步功能时,那么你可能不会从并行化你的流中得到任何好处,因为你的并行流经常会在这个同步障碍上等待。

当你调用阻塞的 i/o 操作时也会出现同样的问题。

image.png

就这一点而言,使用基于 I/O 的源作为流也是众所周知的,因为数据是按顺序读取的,所以这样的源很难被分割。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3168 引用 • 8207 回帖 • 1 关注
  • lamda
    1 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 370 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖 • 1 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    4 引用 • 16 回帖 • 1 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    941 引用 • 1458 回帖 • 154 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    35 引用 • 35 回帖
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 383 回帖 • 1 关注
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    15 引用 • 7 回帖 • 9 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 349 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    164 引用 • 407 回帖 • 526 关注
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    18657 引用 • 69515 回帖
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    75 引用 • 145 回帖
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 42 关注
  • uTools

    uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。

    5 引用 • 13 回帖
  • 深度学习

    深度学习(Deep Learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。

    40 引用 • 40 回帖
  • WordPress

    WordPress 是一个使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设自己的博客。也可以把 WordPress 当作一个内容管理系统(CMS)来使用。WordPress 是一个免费的开源项目,在 GNU 通用公共许可证(GPLv2)下授权发布。

    45 引用 • 113 回帖 • 316 关注
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 548 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    60 引用 • 287 回帖
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    5 引用 • 15 回帖 • 222 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    140 引用 • 441 回帖
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    20 引用 • 245 回帖 • 229 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 742 关注
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    129 引用 • 793 回帖
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    330 引用 • 614 回帖 • 1 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 55 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 177 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    14 引用 • 7 回帖 • 2 关注
  • 新人

    让我们欢迎这对新人。哦,不好意思说错了,让我们欢迎这位新人!
    新手上路,请谨慎驾驶!

    51 引用 • 226 回帖