fork join框架_Fork / Join框架vs.并行流vs.ExecutorService:最终的Fork / Join基准

fork join框架

Fork / Join框架在不同配置下如何工作?

就像即将到来的《星球大战》(Star Wars)一样,围绕Java 8并行性的批评也充满了兴奋。 并行流的语法糖带来了一些炒作,就像我们在预告片中看到的新型光剑一样。 现在,有了许多使用Java进行并行处理的方法,我们希望了解性能优势和并行处理的危险。 经过260多次测试运行后,从数据中获得了一些新见解,我们希望在本文中与您分享。

分叉/加入:分叉唤醒

分叉/加入:分叉唤醒

ExecutorService与Fork / Join Framework与并行流

很久以前,在一个遥远的星系中……。 我的意思是,大约10年前,并发只能通过3rd party库在Java中使用。 随后出现了Java 5,并在语言中引入了java.util.concurrent库,该库在Doug Lea的强烈影响下。 ExecutorService可用并为我们提供了一种处理线程池的直接方法。 当然,java.util.concurrent一直在发展,并且在Java 7中引入了Fork / Join框架,该框架建立在ExecutorService线程池之上。 使用Java 8流,已经为我们提供了使用Fork / Join的简单方法,但对于许多开发人员来说仍然有点谜。 让我们找出它们之间的比较。

我们完成了两项任务,一项是CPU密集型任务,另一项是IO密集型任务,并使用相同的基本功能测试了4种不同的方案。 另一个重要因素是我们用于每个实现的线程数,因此我们也对其进行了测试。 我们使用的机器有8个内核 ,因此我们有4、8、16和32个线程的变种,以大致了解结果的发展方向。 对于每个任务,我们还尝试了单线程解决方案,您不会在图中看到它,因为执行起来要花费更长的时间。 要详细了解测试的运行方式,您可以查看下面的基础部分。 现在,让我们开始吧。

用580万行文本索引6GB文件

在此测试中,我们生成了一个巨大的文本文件,并为索引过程创建了类似的实现。 结果如下所示:

文件索引测试结果

文件索引测试结果

**单线程执行:176,267毫秒,或将近3分钟。
**请注意,图形开始于20000毫秒。

1.更少的线程将使CPU未被使用,太多的线程将增加开销

您在图表中注意到的第一件事是结果开始采用的形状–您仅从这4个数据点就可以了解每个实现的行为。 临界点在8到16个线程之间,因为某些线程在文件IO中处于阻塞状态,并且添加比内核更多的线程有助于更好地利用它们。 当有32个线程进入时,由于额外的开销,性能会变差。

比亚军快1秒:直接使用Fork / Join

除了语法糖(lambdas!我们没有提到lambdas),我们已经看到并行流的性能比Fork / Join和ExecutorService实现的更好。 6GB的文本在24.33秒内被索引。 您可以在这里信任Java来提供最佳结果。

3.但是…并行流也表现最差:唯一的变化超过了30秒

这再次提醒您并行流如何降低您的速度。 假设这种情况发生在已经运行多线程应用程序的计算机上。 在可用线程数量较少的情况下,直接使用Fork / Join实际上比通过并行流要好-5秒的差异,将这两个线程进行比较时大约要付出18%的代价。

4.不要使用图片中带有IO的默认池大小

当为并行流使用默认池大小时,机器上相同数量的内核(此处为8个内核)比16线程版本的性能差了近2秒。 如果使用默认池大小,则要加收7%的罚款。 发生这种情况的原因与阻塞IO线程有关。 还有更多的等待正在进行,因此引入更多的线程可以使我们更多地使用所涉及的CPU内核,而其他线程可以等待调度而不是空闲。

如何更改并行流的默认Fork / Join池大小? 您可以使用JVM参数更改常见的Fork / Join池大小:

-Djava.util.concurrent.ForkJoinPool.common.parallelism=16

(默认情况下,所有Fork / Join任务都使用一个公共静态池,其大小与内核数相同。这样做的好处是,通过在不使用期间为其他任务回收线程,从而减少了资源使用。)

或者…您可以使用此技巧并在自定义Fork / Join池中运行并行流。 这将覆盖通用的Fork / Join池的默认用法,并允许您使用自己设置的池。 偷偷摸摸。 在测试中,我们使用了公共池。

5.单线程性能比最佳结果差7.25倍

并行性提供了7.25倍的改进,并且考虑到该机器具有8个内核,因此非常接近理论上的8倍预测! 我们可以将其余的归因于开销。 话虽如此,即使我们测试的最慢的并行性实现(这次是具有4个线程的并行流(30.24sec)),其性能也比单线程解决方案(176.27sec)好5.8倍。

检查数字是否为质数

在下一轮测试中,我们完全消除了IO,并检查了确定一个真正大的数字是否为素数所需的时间。 多大? 19位数字 。 1,530,692,068,127,007,263或换句话说:五百一十九亿四千三百四十四万亿三千三百八十亿八千八百八十三万三千三百三十。 啊,让我呼吸一下。 无论如何,除了运行到其平方根之外,我们没有使用任何优化,因此即使我们的大数没有除以2只是为了延长处理时间,我们也检查了所有偶数。 剧透警告:这是首要的,因此每个实现都运行相同数量的计算。

结果是这样的:

素数测试结果

素数测试结果

**单线程执行:118,127毫秒,或将近2分钟。
**请注意,图形开始于20000毫秒

1. 8和16个线程之间的差异较小

与IO测试不同,这里没有IO调用,因此8和16线程的性能几乎是相似的,除了Fork / Join解决方案。 实际上,我们已经运行了更多的测试集,以确保由于这种“异常”而在这里获得了良好的结果,但事实证明,一次又一次的相似。 我们很高兴在下面的评论部分中听到您对此的想法。

2.所有方法的最佳结果相似

我们看到所有实现都分享了大约28秒的相似最佳结果。 无论我们尝试采用哪种方法,结果都相同。 这并不意味着我们对使用哪种方法都无所谓。 查看下一个见解。

3.并行流比其他实现更好地处理线程重载

这是更有趣的部分。 通过该测试,我们再次看到运行16个线程的最高结果来自使用并行流。 此外,在此版本中,使用并行流是线程号所有变体的一个好选择。

4.单线程性能比最佳结果低4.2倍

此外,在运行计算密集型任务时使用并行性的好处几乎比使用文件IO的IO测试要差2倍。 这是有道理的,因为它是CPU密集型测试,与之前的测试不同,我们可以通过减少内核等待被IO阻塞的线程的时间来获得额外的好处。

结论

我建议您从源头上了解何时使用并行流的更多信息,并在您使用Java进行并行化时随时进行仔细的判断。 最好的方法是在暂存环境中运行与这些测试类似的测试,在此环境中您可以尝试并更好地了解要面对的挑战。 您必须注意的因素当然是运行的硬件(以及要测试的硬件)以及应用程序中的线程总数。 这包括公用的Fork / Join池和团队中其他开发人员正在处理的代码。 因此,在添加自己的并行性之前,请尝试检查它们并获得应用程序的完整视图。

基础工作

为了运行此测试,我们使用了具有8个vCPU和15GB RAM的EC2 c3.2xlarge实例。 vCPU意味着存在超线程,因此实际上我们在这里有4个物理内核,每个物理内核都像2个内核一样工作。就OS调度程序而言,我们在这里有8个内核。 为了尽可能使它公平,每个实现都运行了10次,并且我们采用了运行2到9的平均运行时间。这是260次测试运行,! 另一重要的是处理时间。 我们选择的任务要花费20秒钟以上的时间,因此差异更容易发现,并且不受外部因素的影响。

下一步是什么?

原始结果可在此处获得 ,代码在GitHub上 。 请随时修改它,并让我们知道您得到什么样的结果。 如果您对我们错过的结果有更多有趣的见解或解释,我们很乐意阅读并添加到帖子中。

翻译自: https://www.javacodegeeks.com/2015/01/forkjoin-framework-vs-parallel-streams-vs-executorservice-the-ultimate-forkjoin-benchmark.html

fork join框架

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

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

相关文章

scanf在c语言中的作用是什么?

scanf()函数scanf()是C语言中的一个输入函数。与printf函数一样,都被声明在头文件stdio.h里,因此在使用scanf函数时要加上#include 。(在有一些实现中,printf函数与scanf函数在使用时可以不使用预编译命令#include 。)…

amqp rabbitmq_通过Spring Integration和RabbitMQ获得高可用性的AMQP支持的消息通道

amqp rabbitmqSpring Integration消息通道默认情况下将消息存储在内存中。 这是因为内存速度快,易于实现,并且不会增加网络成本。 但是,在某些情况下,这可能会引起问题,因为如果应用程序崩溃或服务器意外关闭&#xff…

虚函数和纯虚函数的区别是什么?

虚函数(impure virtual)  C 的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现。  子类可以重写父类的虚函数实现子类的特殊化。  如下就是一个父类中的虚函数:class A{public: virtual void ss(…

java 编译 器 ide_Java 8发布一年后,IDE和编译器尚未完全就绪

java 编译 器 ide一年前, 2014年3月18日 ,发布了Java SE 8,并通过lambda表达式和streams API带来了功能性编程的幸福。 这对于我们所有的Java生态系统都是个好消息,许多人已经升级到Java8。Stack Overflow已经提出了将近2500个有关…

windows.h有哪些函数

C语言windows.h库的常用函数1:FindWindow函数该函数可以通过窗口类名或者窗口标题名来查找特定窗口句柄,返回值是窗口的句柄(在Windows中,句柄是一个系统内部数据结构的引用。例如当你操作一个窗口,或说是一个Delphi窗…

yxcms安装环境php,Windows7下PHP开发环境安装配置图文方法

操作系统:Windows 7 UltimateWEB服务器:IIS 6.1(内部版本7600)。数据库:MySql5.0.67PHP版本:5.2.13我还担心Win7下可能会不兼容,结果是一点问题都没有。一、安装MySql数据库客户端工具Navicat(导航猫)在这里下载&#…

apache camel_您的Apache Camel应用程序现在包括现成的文档

apache camel几个月前,我在博客中发布了有关即将发布的2.15版本的功能,该功能包括获取有关在端点上配置的每个属性的详细信息的功能-Apache Camel,请向我解释这些端点选项的含义 。 我们继续沿着这条道路前进,今天我们将其从端点…

C语言的三种基本程序结构是什么

一、顺序结构表达式语句、空语句、函数调用语句、复合语句程序举例&#xff1a;从键盘输入一个大写字母&#xff0c;要求改用小写字母输出。#includeint main(){ char x,y; scanf("%c",&x); if(x > A && x < Z) { …

C语言中的指针有什么作用

C语言中的指针的作用是&#xff1a;通过指针不仅可以对数据本身&#xff0c;还可以对存储数据的变量地址进行操作。指针就是内存地址&#xff0c;指针变量是用来存放内存地址的变量。指针定义&#xff1a;指针&#xff0c;是C语言中的一个重要概念及其特点&#xff0c;也是掌握…

rsa php openssl,openssl rsa 使用简介

openssl命令的用法密钥的生成a. 生成非对称密钥对openssl genrsa -out rsa.keyb. 指定生成的密钥的位数,默认512openssl genrsa -out rsa_2048.key 2048c. 为私钥添加密码 (一般都不用)openssl genrsa -out rsa_des3.key -des3密钥的查看d. 查看私钥openssl rsa -in rsa.keye. …

C语言标识符有哪三类

C语言标识符有关键字、用户标识符、预定义标识符三类。C语言规定&#xff0c;标识符只能由字母、数字和下划线组成&#xff0c;并且第一个字符必须是字母或下划线&#xff0c;不能是数字。C语言中的标识符可分为关键字、用户标识符、预定义标识符三类。C语言规定&#xff0c;标…

Hibernate锁定模式– PESSIMISTIC_FORCE_INCREMENT锁定模式如何工作

介绍 在上 一篇 文章中 &#xff0c;我介绍了OPTIMISTIC_FORCE_INCREMENT锁定模式&#xff0c;并将其应用于将子实体版本更改传播到锁定的父实体。 在本文中&#xff0c;我将介绍PESSIMISTIC_FORCE_INCREMENT锁定模式&#xff0c;并将其与乐观的锁定模式进行比较。 相像多于不…

C语言中字符串的结束标志是什么

C语言中字符串的结束标志是【\0】。C语言中没有专门的字符串变量&#xff0c;通常用一个字符数组来存放一个字符串&#xff0c;字符串总是以【\0】作为结束符。\0就是8位的00000000&#xff0c;因为字符类型中并没有对应的这个字符&#xff0c;所以这么写。\0就是 字符串结束标…

php 5.6.27 在某些机器上正常,在 Windows 10 64、PHP 5.6 下重命名中文名文件,提示错误的解决...

1、重命名某个目录中的文件名&#xff0c;其代码&#xff0c;如图1图12、报错&#xff1a;rename(E:/wwwroot/avatar/BEIJI/侯森.jpg,E:/wwwroot/avatar/BEIJI/378477.jpg): ϵͳ&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ָ&#xfffd;&#xfffd;&#xfffd;&am…

c语言中字符常量是什么?

c语言中字符常量是什么&#xff1f;字符常量&#xff1a;一个用单引号括起来的单个字符&#xff08;或字符转义序列或三字母词&#xff09;实质&#xff08;含义&#xff09;&#xff1a;是一个整形值。属于四大基本数据类型&#xff08;分别是整型&#xff0c;浮点型&#xff…

Hibernate锁定模式– OPTIMISTIC_FORCE_INCREMENT锁定模式如何工作

介绍 在我以前的文章中 &#xff0c;我解释了OPTIMISTIC锁定模式是如何工作的&#xff0c;以及它如何帮助我们同步外部实体状态更改。 在本文中&#xff0c;我们将介绍OPTIMISTIC_FORCE_INCREMENT锁定模式的使用模式。 使用LockModeType.OPTIMISTIC &#xff0c;将在当前正在运…

c语言的输入输出语句有哪些?

c语言的输入输出语句有&#xff1a;“getchar(void);”和“putchar(int c);”、“scanf("格式控制字符串",地址列表);”和“printf("格式控制字符串",输出列表);”、“gets()”和“puts()”等等。一&#xff1a;控制台输入输出(1)字符数据的输入/输出字符输…

primefaces_PrimeFaces:在动态生成的对话框中打开外部页面

primefaces我已经在即将出版的PrimeFaces Cookbook版本2中写过一篇食谱的博客。 在这篇文章中&#xff0c;我想发表第二篇关于一个名为Dialog Framework的小型框架的文章。 我个人喜欢它&#xff0c;因为我记得我为使用Struts框架付出同样的代价。 当您想将外部页面加载到弹出窗…

c语言源文件经过编译后生成文件的后缀是什么?

c语言源文件经过编译后&#xff0c;生成文件的后缀是“.obj”。C语言源文件后缀名是“.c”&#xff0c;编译生成的文件后缀名是“.obj”&#xff0c;连接后可执行文件的后缀名是“.exe”。C语言创建程序的步骤&#xff1a;编辑&#xff1a;就是创建和修改C程序的源代码-我们编写…

java编译器jdk版本_以编程方式确定Java类的JDK编译版本

java编译器jdk版本当需要确定使用哪个JDK版本来编译特定的Java .class文件时&#xff0c; 通常使用的方法是使用javap并在javap输出中查找列出的“主要版本”。 我在我的博客文章Autoboxing&#xff0c;Unboxing和NoSuchMethodError中引用了这种方法&#xff0c;但是在继续以编…