Java8实战-总结29

Java8实战-总结29

  • 并行数据处理与性能
    • 并行流
      • 将顺序流转换为并行流
      • 测量流性能

并行数据处理与性能

到目前为止,Stream接口最重要的好处是可以对这些集合执行操作流水线,能够自动利用计算机上的多个内核。

例如,在Java 7之前,并行处理数据集合非常麻烦。第一,需要明确地把包含数据的数据结构分成若干子部分。第二,要给每个子部分分配一个独立的线程。第三,需要在恰当的时候对它们进行同步来避免不希望出现的竞争条件,等待所有线程完成,最后把这些部分结果合并起来。Java 7引入了一个叫作分支/合并的框架,让这些操作更稳定、更不易出错。

Stream接口允许声明性地将顺序流变为并行流。了解并行流内部是如何工作的很重要,因为如果忽视这一方面,就可能因误用而得到意外的(很可能是错的)结果。

并行流

stream接口可以让你非常方便地处理它的元素:可以通过对收集源调用parallelStream方法来把集合转换为并行流。并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。这样一来,就可以自动把给定操作的工作负荷分配给多核处理器的所有内核,让它们都忙起来。

假设需要写一个方法,接受数字n作为参数,并返回从1到给定参数的所有数字的和。一个直接的方法是生成一个无穷大的数字流,把它限制到给定的数目,然后用对两个数字求和的Binaryoperator来归约这个流,如下所示:

	public static long sequentialSum(long n) {return Stream.iterate(1L, i -> i + 1) //生成自然数无限流.limit(n) //限制到前n个数.reduce(0L, Long::sum); //对所有数字求和来归纳流

用更为传统的Java术语来说,这段代码与下面的迭代等价:

	public static long iterativesum(long n) {long result = 0;for(1ong i = 1L; i <= n; i++) {result += i;}return result;}

这似乎是利用并行处理的好机会,特别是n很大的时候。那怎么入手呢?要对结果变量进行同步吗?用多少个线程呢?谁负责生成数呢?谁来做加法呢?

用并行流的话,这问题就简单多了。

将顺序流转换为并行流

可以把流转换成并行流,从而让前面的函数归约过程(也就是求和)并行运行——对顺序流调用parallel方法:

	public static long parallelSum(long n) {return Stream.iterate(1L, i -> i + 1).limit(n)·paralle1() //将流转换为并行流.reduce(0L, Long::sum);

在上面的代码中的不同之处在于Stream在内部分成了几块。因此可以对不同的块独立并行进行归纳操作,如下图所示。最后,同一个归纳操作会将各个子流的部分归纳结果合并起来,得到整个原始流的归纳结果。
在这里插入图片描述
请注意,在现实中,对顺序流调用parallel方法并不意味着流本身有任何实际的变化。它在内部实际上就是设了一个boolean标志,表示你想让调用parallel之后进行的所有操作都并行执行。类似地,你只需要对并行流调用sequential方法就可以把它变成顺序流。请注意,你可能以为把这两个方法结合起来,就可以更细化地控制在遍历流时哪些操作要并行执行,哪些要顺序执行。例如,可以这样做:

stream.parallel().filter(...).sequential().map(...)·parallel().reduce();

但最后一次parallelsequential调用会影响整个流水线。在本例中,流水线会并行执行,因为最后调用的是它。

回到我们的数字求和练习,我们说过,在多核处理器上运行并行版本时,会有显著的性能提升。现在你有三个方法,用三种不同的方式(迭代式、顺序归纳和并行归纳)做完全相同的操作,让我们看看谁最快吧!

测量流性能

虽然声称并行求和方法应该比顺序和迭代方法性能好。然而在软件工程上,靠猜绝对不是什么好办法,特别是在优化性能时,你应该始终遵循三个黄金规则:测量,测量,再测量。为此,可以开发一个方法,如下所示,测量对前n个自然数求和的函数的性能:

public long measureSumPerf(Function<Long, Long> adder, long n) {long fastest = Long.MAX_VALUE;for(int i = 0; i < 10; i++) {long start = System.nanoTime();long sum = adder.apply(n);long duration =(System.nanoTime() - start) / 1_000_000;System.out.println("Result:"+ sum);if(duration < fastest) fastest = duration;}return fastest;
}

这个方法接受一个函数和一个long作为参数。它会对传给方法的long应用函数10次,记录每次执行的时间(以毫秒为单位),并返回最短的一次执行时间。假设你把先前开发的所有方法都放进了一个名为ParallelStreams的类,你就可以用这个框架来测试顺序加法器函数对前一千万个自然数求和要用多久:

System.out.println("Sequential sum done in:" + measureSumPerf(ParallelStreams::sequentialSum,10_000_000) + " msecs");

请注意,我们对这个结果应持保留态度。影响执行时间的因素有很多,比如你的电脑支持多少个内核。你可以在自己的机器上跑一下这些代码。我们运行它,输出是这样的:

Sequential sum done in: 97 msecs

用传统for循环的迭代版本执行起来应该会快很多,因为它更为底层,更重要的是不需要对原始类型做任何装箱或拆箱操作。如果你试着测量它的性能,

System.out.println("Iterative sum done in:" +
measureSumPerf(ParallelStreams::iterativesum, 10_000_000) + " msecs");

将得到:

Iterative sum done in: 2 msecs

现在来对函数的并行版本做测试:

System.out.println("Parallel sum done in:" + measureSumPerf(ParallelStreams::parallelSum, 10_000_000) + " msecs");

看看会出现什么情况:

Parallel sum done in: 164 msecs

这相当令人失望,求和方法的并行版本比顺序版本要慢很多。如何解释这个意外的结果呢?这里实际上有两个问题:

  • iterate生成的是装箱的对象,必须拆箱成数字才能求和;
  • 我们很难把iterate分成多个独立块来并行执行。

第二个问题更有意思一点,因为你必须意识到某些流操作比其他操作更容易并行化。具体来说,iterate很难分割成能够独立执行的小块,因为每次应用这个函数都要依赖前一次应用的结果,如下图所示。
在这里插入图片描述
这意味着,在这个特定情况下,归纳进程不是像上面图中那样进行的;整张数字列表在归纳过程开始时没有准备好,因而无法有效地把流划分为小块来并行处理。把流标记成并行,你其实是给顺序处理增加了开销,它还要把每次求和操作分到一个不同的线程上。

这就说明了并行编程可能很复杂,有时候甚至有点违反直觉。如果用得不对(比如采用了一个不易并行化的操作,如iterate),它甚至可能让程序的整体性能更差,所以在调用那个看似神奇的parallel操作时,了解背后到底发生了什么是很有必要的。

使用更有针对性的方法
那到底要怎么利用多核处理器,用流来高效地并行求和呢?LongStream.rangeClosed方法与iterate相比有两个优点。

  • LongStream.rangeClosed直接产生原始类型的long数字,没有装箱拆箱的开销。
  • LongStream.rangeClosed会生成数字范围,很容易拆分为独立的小块。例如,范围1~ 20可分为1~ 5、6~ 10、11~ 15和16~20。

让我们先看一下它用于顺序流时的性能如何,看看拆箱的开销到底要不要紧:

public static long rangedsum(long n) {return LongStream.rangeclosed(1, n).reduce(OL, Long::sum);
}

这一次的输出是:

Ranged sum done in: 17 msecs

这个数值流比前面那个用iterate工厂方法生成数字的顺序执行版本要快得多,因为数值流避免了非针对性流那些没必要的自动装箱和拆箱操作。由此可见,选择适当的数据结构往往比并行化算法更重要。但要是对这个新版本应用并行流呢?

public static long parallelRangedsum(long n) {return LongStream.rangeClosed(1, n)·parallel().reduce(0L, Long::sum);
}

现在把这个函数传给测试方法:

System.out.println("Parallel range sum done in:" + measureSumPerf(ParallelStreams::parallelRangedsum, 10_000_000) + " msecs");

会得到:

Parallel range sum done in: 1 msecs

终于,得到了一个比顺序执行更快的并行归纳,因为这一次归纳操作可以像上面图中那样执行了。这也表明,使用正确的数据结构然后使其并行工作能够保证最佳的性能。

尽管如此,请记住,并行化并不是没有代价的。并行化过程本身需要对流做递归划分,把每个子流的归纳操作分配到不同的线程,然后把这些操作的结果合并成一个值。但在多个内核之间移动数据的代价也可能比想的要大,所以很重要的一点是要保证在内核中并行执行工作的时间比在内核之间传输数据的时间长。总而言之,很多情况下不可能或不方便并行化。然而,在使用并行Stream加速代码之前,必须确保用得对;如果结果错了,算得快就毫无意义了。

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

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

相关文章

如何开发你的第一个Vue.js插件:完整指南

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

ModbusTCP服务端

1在Device下&#xff0c;添加设备net&#xff1a; 公交车。 2在net下添加 ModbusTCP

【深度学习】 Python 和 NumPy 系列教程(十九):Matplotlib详解:2、3d绘图类型(5)3D等高线图(3D Contour Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 3D线框图&#xff08;3D Line Plot&#xff09; 2. 3D散点图&#xff08;3D Scatter Plot&#xff09; 3. 3D条形图&#xff08;3D Bar Plot&#xff09; 4. 3D曲面图…

MySQL数据库详解 三:索引、事务和存储引擎

文章目录 1. 索引1.1 索引的概念1.2 索引的作用1.3 如何实现索引1.4 索引的缺点1.5 建立索引的原则依据1.6 索引的分类和创建1.6.1 普通索引1.6.2 唯一索引1.6.3 主键索引1.6.4 组合索引1.6.5 全文索引 1.7 查看索引1.8 删除索引 2. 事务2.1 事务的概念2.2 事务的ACID特性2.2.1…

人机中的事实与价值时空、排序

人机结合智能与事实价值融合分析确实是未来解决复杂疑难问题的基本策略之一。该策略利用人类智慧和机器智能相结合&#xff0c;结合有效的事实和价值分析方法&#xff0c;以更全面、准确、高效地解决问题。 通过人机结合&#xff0c;可以充分发挥人类的主观能动性、判断力和创造…

2023年 python结合excel实现快速画图(零基础快速入门)

目录 1.适用人群 2.环境配置 3.基本用法 3.1 数据读取 3.2 数据分析 3.3 数据组装 3.4 制表&#xff1a; 4.快速提升 5.效果展示 1.适用人群 电脑有python环境&#xff0c;会python基本使用&#xff0c;需要短时间内完成大量画图任务的数据分析的人群。&#xff08;有…

JDK17特性

文章目录 一、JAVA17概述二、语法层面的变化1.密封类2.switch模式匹配&#xff08;预览&#xff09; 三、API层面变化1.Vector API&#xff08;第二个孵化器&#xff09;2.特定于上下文的反序列化过滤器 四、其他变化1.恢复始终严格的浮点语义2.JEP 增强型伪随机数生成器3.JEP …

C++库函数——map与set

目录 1.关联式容器是什么&#xff1f; 2.键值对 3.set ①set的介绍 ②set的模板参数列表 ③set的构造 ④set的迭代器 ⑤set的容量 ⑥set的修改与操作 ⑦set的使用举例 4.multiset ①multiset的介绍 ②multiset的使用举例 5.map ①map的介绍 ②map的模版参数列表…

HuggingFace Transformer

NLP简介 HuggingFace简介 hugging face在NLP领域最出名&#xff0c;其提供的模型大多都是基于Transformer的。为了易用性&#xff0c;Hugging Face还为用户提供了以下几个项目&#xff1a; Transformers(github, 官方文档): Transformers提供了上千个预训练好的模型可以用于不…

基于讯飞人脸算法(调用API进行人脸比对)

先看结果 必须遥遥领先 所需准备 这里我调用了&#xff1a; 人脸比对 API 文档 | 讯飞开放平台文档中心https://www.xfyun.cn/doc/face/xffaceComparisonRecg/API.html#%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E 代码里所涉及的APPID、APISecret、APIKey 皆从讯飞的控制台获取&…

市场,只能被操纵,不能被战胜

所谓市场&#xff0c;不过是千千万参与主体各自独立意志、自主行动所形成的复杂混沌的互动结果。价格&#xff0c;则是这一复杂混沌系统的涌现现象。 无数在市场中追风打浪的人&#xff0c;总是梦想着自己有朝一日能够战胜市场&#xff0c;获得超额回报。于是他们绞尽脑汁&…

Mybatis学习笔记3 在Web中应用Mybatis

Mybatis学习笔记2 增删改查及核心配置文件详解_biubiubiu0706的博客-CSDN博客 技术栈:HTMLServletMybatis 学习目标: 掌握mybatis在web应用中如何使用 Mybatis三大对对象的作用域和生命周期 关于Mybatis中三大对象的作用域和生命周期、 官网说明 ThreadLocal原理及使用 巩…

JAVA入坑之嵌套类

一、嵌套类入门 1.1概述 Java嵌套类是指在一个类中定义另一个类的一种方式&#xff0c;它可以提高代码的可读性、可维护性和封装性。Java嵌套类分为两种类型&#xff1a;静态嵌套类和非静态嵌套类。 静态嵌套类&#xff1a;Static nested classes,即类前面有static修饰符 非静…

【论文解读】Faster sorting algorithm

一、简要介绍 基本的算法&#xff0c;如排序或哈希&#xff0c;在任何一天都被使用数万亿次。随着对计算需求的增长&#xff0c;这些算法的性能变得至关重要。尽管在过去的2年中已经取得了显著的进展&#xff0c;但进一步改进这些现有的算法路线的有效性对人类科学家和计算方法…

2023-09-17 LeetCode每日一题(打家劫舍 II)

2023-09-17每日一题 一、题目编号 213. 打家劫舍 II二、题目链接 点击跳转到题目位置 三、题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 &#xff0c;这意味着第一个房屋和最后一个房…

《golang设计模式》第二部分·结构型模式-05-门面模式Facade)

文章目录 1. 概述1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.2 类图 1. 概述 门面&#xff08;Facade&#xff09;向客户端提供使用子系统的统一接口&#xff0c;用于简化客户端使用子系统的操作。 1.1 角色 门面角色&#xff08;Facade&#xff09; 客户端可以调用的接…

7、DVWA——SQL盲注

文章目录 一、概述二、low2.1 通关思路&#xff08;布尔盲注&#xff09;&#xff08;1&#xff09;判断是否存在SQL注入漏洞&#xff08;2&#xff09;判断属于数字型注入还是字符型注入&#xff08;3&#xff09;判断结果集中的字段数&#xff08;4&#xff09;猜数据库名长度…

ArcGIS Pro将SHP文件转CAD并保留图层名称

相信大家应该都使用过ArcGIS将SHP文件转CAD格式&#xff0c;转换过后所有的要素都在一个图层内&#xff0c;那么有没有办法将SHP文件某个字段的值作为CAD的图层名字呢&#xff0c;答案是肯定的&#xff0c;这里就为大家介绍一下ArcGIS Pro转CAD文件并且保留图层名称的方法&…

Windows编程dll基本知识点

前言 本篇博客主要是记录windows系统下dll开发的相关基本知识点&#xff0c;并使用相关分析工具分析&#xff0c;有利于初学者学习&#xff0c;更是为开发者查缺补漏&#xff1b; 使用dumpbin查看dll,lib,exe相关信息 VS编译器提供了查看链接库相关的工具&#xff0c;安装后…

【c++GDAL】IHS融合

【c&GDAL】IHS融合 基于IHS变换融合&#xff0c;实现多光谱和全色影像之间的融合。IHS分别指亮度(I)、色度(H)、饱和度(S)。IHS变换融合基于亮度I进行变换&#xff0c;色度和饱和度空间保持不变。 IHS融合步骤&#xff1a; &#xff08;1&#xff09;将多光谱RGB影像变换到…