为什么 Files.list() 并行流的执行速度比使用 Collection.parallelStream() 慢得多?

2024-02-08

以下代码片段是获取目录列表、对每个文件调用提取方法并将生成的药物对象序列化为 xml 的方法的一部分。

try(Stream<Path> paths = Files.list(infoDir)) {
    paths
        .parallel()
        .map(this::extract)
        .forEachOrdered(drug -> {
            try {
                marshaller.write(drug);
            } catch (JAXBException ex) {
                ex.printStackTrace();
            }
        });
}

这是完全相同的代码,执行完全相同的操作,但使用普通的.list()调用以获取目录列表并调用.parallelStream()在结果列表中。

Arrays.asList(infoDir.toFile().list())
    .parallelStream()
    .map(f -> infoDir.resolve(f))
    .map(this::extract)
    .forEachOrdered(drug -> {
        try {
            marshaller.write(drug);
        } catch (JAXBException ex) {
            ex.printStackTrace();
    }
});

我的机器是四核 MacBook Pro,Java v 1.8.0_60(内部版本 1.8.0_60-b27)。

我正在处理约 7000 个文件。 3次运行的平均值:

第一个版本: 和.parallel():20秒。没有.parallel():41秒

第二个版本: 和.parallelStream():12秒。和.stream():41秒。

并行模式下的那 8 秒似乎是一个巨大的差异,因为extract从流中读取并完成所有繁重工作的方法write进行最终写入的调用没有改变。


问题是 Stream API 的当前实现以及IteratorSpliterator对于未知大小的源,严重地将此类源拆分为并行任务。您很幸运拥有超过 1024 个文件,否则您将根本没有并行化的好处。当前的 Stream API 实现考虑了estimateSize()返回值来自Spliterator. The IteratorSpliterator未知大小的回报Long.MAX_VALUE在 split 之前,其后缀总是返回Long.MAX_VALUE以及。其分裂策略如下:

  1. 定义当前批量大小。当前公式是从 1024 个元素开始,然后按算术增加(2048、3072、4096、5120 等),直到MAX_BATCH已达到大小(即 33554432 个元素)。
  2. 将输入元素(在您的情况下为路径)消耗到数组中,直到达到批量大小或输入耗尽。
  3. 返回一个ArraySpliterator迭代创建的数组作为前缀,将其自身作为后缀。

假设您有 7000 个文件。 Stream API 要求估计大小,IteratorSpliterator回报Long.MAX_VALUE。好的,Stream API 询问IteratorSpliterator为了分割,它从底层收集 1024 个元素DirectoryStream到数组并拆分为ArraySpliterator(估计大小 1024)及其本身(估计大小仍然是Long.MAX_VALUE). As Long.MAX_VALUE远大于 1024,Stream API 决定继续分割较大的部分,甚至不尝试分割较小的部分。所以整体的分裂树是这样的:

                     IteratorSpliterator (est. MAX_VALUE elements)
                           |                    |
ArraySpliterator (est. 1024 elements)   IteratorSpliterator (est. MAX_VALUE elements)
                                           |        |
                           /---------------/        |
                           |                        |
ArraySpliterator (est. 2048 elements)   IteratorSpliterator (est. MAX_VALUE elements)
                                           |        |
                           /---------------/        |
                           |                        |
ArraySpliterator (est. 3072 elements)   IteratorSpliterator (est. MAX_VALUE elements)
                                           |        |
                           /---------------/        |
                           |                        |
ArraySpliterator (est. 856 elements)    IteratorSpliterator (est. MAX_VALUE elements)
                                                    |
                                        (split returns null: refuses to split anymore)

因此,之后有 5 个并行任务需要执行:实际上包含 1024、2048、3072、856 和 0 个元素。请注意,即使最后一个块有 0 个元素,它仍然报告它估计有Long.MAX_VALUE元素,因此 Stream API 会将其发送到ForkJoinPool以及。不好的是 Stream API 认为进一步分割前四个任务是没有用的,因为它们的估计大小要小得多。因此,您得到的输入分割非常不均匀,最多使用四个 CPU 核心(即使您有更多核心)。如果每个元素的处理对于任何元素都花费大致相同的时间,那么整个过程将等待最大的部分(3072 个元素)完成。因此,您可能获得的最大加速是 7000/3072=2.28 倍。因此,如果顺序处理需要 41 秒,那么并行流将花费大约 41/2.28 = 18 秒(接近实际数字)。

您的解决方案完全没问题。请注意,使用Files.list().parallel()你也有所有的输入Path存储在内存中的元素(在ArraySpliterator对象)。因此,如果您手动将它们转储到List。数组支持的列表实现,例如ArrayList(目前由Collectors.toList())可以毫无问题地均匀分割,从而带来额外的加速。

为什么这样的情况没有优化呢?当然,这不是不可能的问题(尽管实现可能相当棘手)。对于 JDK 开发人员来说,这似乎并不是一个优先考虑的问题。邮件列表中对此主题进行了多次讨论。您可以阅读 Paul Sandoz 的留言here http://mail.openjdk.java.net/pipermail/core-libs-dev/2015-July/034539.html他对我的优化工作发表了评论。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Files.list() 并行流的执行速度比使用 Collection.parallelStream() 慢得多? 的相关文章

  • Java:如何从转义的 URL 获取文件?

    我收到了一个定位本地文件的 URL 事实上我收到的 URL 不在我的控制范围内 URL 按照 RFC2396 中的定义进行有效转义 如何将其转换为 Java File 对象 有趣的是 URL getFile 方法返回一个字符串 而不是文件
  • Android在排序列表时忽略大小写

    我有一个名为路径的列表 我目前正在使用以下代码对字符串进行排序 java util Collections sort path 这工作正常 它对我的 列表进行排序 但是它以不同的方式处理第一个字母的情况 即它用大写字母对列表进行排序 然后用
  • 比较两个文本文件的最快方法是什么,不将移动的行视为不同

    我有两个文件非常大 每个文件有 50000 行 我需要比较这两个文件并识别更改 然而 问题是如果一条线出现在不同的位置 它不应该显示为不同的 例如 考虑这个文件A txt xxxxx yyyyy zzzzz 文件B txt zzzzz xx
  • 如何在不超过最大值的情况下增加变量?

    我正在为学校开发一个简单的视频游戏程序 我创建了一个方法 如果调用该方法 玩家将获得 15 点生命值 我必须将生命值保持在最大值 100 并且由于我目前的编程能力有限 我正在做这样的事情 public void getHealed if h
  • Cassandra java驱动程序协议版本和连接限制不匹配

    我使用的java驱动程序版本 2 1 4卡桑德拉版本 dsc cassandra 2 1 10cql 的输出给出以下内容 cqlsh 5 0 1 Cassandra 2 1 10 CQL spec 3 2 1 Native protocol
  • 我可以使用子接口重新编译公共 API 并保持二进制兼容性吗?

    我有一个公共 API 在多个项目中多次使用 public interface Process
  • 使用 AES SecretKey 的 Java KeyStore setEntry()

    我目前正在 Java 中开发一个密钥处理类 特别是使用 KeyStore 我正在尝试使用 AES 实例生成 SecretKey 然后使用 setEntry 方法将其放入 KeyStore 中 我已经包含了代码的相关部分 The KS Obj
  • 在 S3 中迭代对象时出现“ConnectionPoolTimeoutException”

    我已经使用 aws java API 一段时间了 没有遇到太多问题 目前我使用的是库 1 5 2 版本 当我使用以下代码迭代文件夹内的对象时 AmazonS3 s3 new AmazonS3Client new PropertiesCred
  • 将 SignedHash 插入 PDF 中以进行外部签名过程 -workingSample

    遵循电子书第 4 3 3 节 PDF 文档的数字签名 https jira nuxeo com secure attachment 49931 digitalsignatures20130304 pdf 我正在尝试创建一个工作示例 其中 客
  • 普罗米修斯指标 - 未找到

    我有 Spring Boot 应用程序 并且正在使用 vertx 我想监控服务和 jvm 为此我选择了 Prometheus 这是我的监控配置类 Configuration public class MonitoringConfig Bea
  • Javafx过滤表视图

    我正在尝试使用文本字段来过滤表视图 我想要一个文本字段 txtSearch 来搜索 nhs 号码 名字 姓氏 和 分类类别 我尝试过在线实施各种解决方案 但没有运气 我对这一切仍然很陌生 所以如果问得不好 我深表歉意 任何帮助将不胜感激 我
  • 有没有一种快速方法可以从 Jar/war 中删除文件,而无需提取 jar 并重新创建它?

    所以我需要从 jar war 文件中删除一个文件 我希望有类似 jar d myjar jar file I donot need txt 的内容 但现在我能看到从 Linux 命令行执行此操作的唯一方法 不使用 WinRAR Winzip
  • 如何知道抛出了哪个异常

    我正在对我们的代码库进行审查 有很多这样的陈述 try doSomething catch Exception e 但我想要一种方法来知道 doSomething 抛出了哪个异常 在 doSomething 的实现中没有 throw 语句
  • 如何在JSTL中调​​用java方法? [复制]

    这个问题在这里已经有答案了 这可能是重复的问题 我只想调用不是 getter 或 setter 方法的方法例如 xyz 类的 makeCall someObj stringvalue Java类 Class XYZ public Strin
  • 测试弱引用

    在 Java 中测试弱引用的正确方法是什么 我最初的想法是执行以下操作 public class WeakReferenceTest public class Target private String value public Targe
  • Netty:阻止调用以获取连接的服务器通道?

    呼吁ServerBootstrap bind 返回一个Channel但这不是在Connected状态 因此不能用于写入客户端 Netty 文档中的所有示例都显示写入Channel从它的ChannelHandler的事件如channelCon
  • HQL Hibernate 内连接

    我怎样才能在 Hibernate 中编写这个 SQL 查询 我想使用 Hibernate 来创建查询 而不是创建数据库 SELECT FROM Employee e INNER JOIN Team t ON e Id team t Id t
  • 具有特定参数的 Spring AOP 切入点

    我需要创建一个我觉得很难描述的方面 所以让我指出一下想法 com x y 包 或任何子包 中的任何方法 一个方法参数是接口 javax portlet PortletRequest 的实现 该方法中可能有更多参数 它们可以是任何顺序 我需要
  • GUI Java 程序 - 绘图程序

    我一直试图找出我的代码有什么问题 这个想法是创建一个小的 Paint 程序并具有红色 绿色 蓝色和透明按钮 我拥有我能想到的让它工作的一切 但无法弄清楚代码有什么问题 该程序打开 然后立即关闭 import java awt import
  • 如何在 JFreeChart 中设置多个系列的线条粗细?

    我创建了很多图表 在他们每个人中我都需要打电话 renderer setSeriesStroke i new BasicStroke 2 0f 对于每个系列 renderer is chart getXYPlot getRenderer 我

随机推荐

  • unicode中字符串的长度不同

    尽管字符串中的字符数相同 但为什么以下字符串的长度不同 echo strlen 馐 馑 馒 馓 馔 馕 首 馗 馘 br echo strlen br Outputs 35 26 第一批字符每个占用 3 个字节 因为它们在 39000 个字
  • 如何删除信号处理程序

    我已经制作了以下信号处理程序 struct sigaction pipeIn pipeIn sa handler updateServer sigemptyset pipeIn sa mask sa sa flags SA RESTART
  • 如何在iPhone上进行Base64编码

    如何在iPhone上进行Base64编码 我发现了一些看起来很有希望的例子 但永远无法让它们在手机上工作 你可以看一个例子here http iosdevelopertips com core services encode decode
  • 图像捕获延迟 - React Native Camera / Expo Camera

    我正在尝试实现与 Facebook 或 Instagram 相同的功能 即时预览相机拍摄的图像 此时 当调用此函数时 我的 take 被正确获取 takePicture async function if this camera this
  • 将 8 字节的小端二进制转换为双精度浮点数

    我有一个二进制文件 我逐字节读取 我遇到一个 8 字节长的部分 包含一个双精度浮点 小端 我不知道如何读取它并通过掩蔽和 或转换正确计算它 具体来说 文件类型是 LAS 但这并不重要 Java有什么技巧吗 您可以使用ByteBuffer h
  • Python Fabric:如何响应键盘输入?

    我想自动响应某些程序提示的某些问题 例如 mysql 提示输入密码 或者 apt 询问 是 或 当我想使用 manage pyrebuild index 重建我的干草堆索引时 对于 MySQL 我可以使用 password 开关 并且我确信
  • 在 MSVC 中处理 __attribute__

    我想知道处理包含 GCC 的代码的最佳方法是什么 attribute 使用 MSVC 时的扩展 以下是处理此问题的安全方法 define attribute x blank should simply ignore thanks to C
  • 如何跨包引用 Android“资产”?

    我有一个以 免费 和 专业 版本发布的 Android 应用程序 我已经使用从两个版本引用的基础 库 项目设置了我的项目 这样我的包集如下所示 com example myapp com example myapp free com exa
  • 如何翻译活动记录模型验证

    当我提交有错误的表单时 它会返回一条错误消息 如何使用 i18n 翻译这些错误消息 我已经翻译了我视图中的所有其他文本 所以我知道 l18n 在 Rails 中是如何工作的 我现在明白了 2 errors prohibited this u
  • 寻找最高的键

    我只是对为什么我的代码不起作用感到困惑 这是我到目前为止的问题和代码 测试运行表明我的答案是错误的 给定字典d 找到字典中最大的key并将对应的值与变量关联起来val of max 例如 给定字典 5 3 4 1 12 2 2 将与val
  • 循环数组和另一个对象中的对象

    我有以下结构 我需要在 React 中获取内部值并通过 我想我需要获取一个值数组 例如 Bitcoin Etherium 并通过它进行映射 我怎样才能实现它 let arr CoinInfo Id 1182 Name BTC FullNam
  • 汇编中的结构或类

    我需要 C 中的结构或类之类的东西 例如 我需要一个带有数组和两个属性 大小和长度 的类以及一些函数 例如append和remove 如何使用宏和过程在汇编中实现这一点 Tasm 支持例如 struc String note without
  • Java流按2个字段排序

    我有需要按 2 个参数排序的项目列表 第一个参数是 orderIndex 并且我使该部分正常工作 参见下面的代码 orderIndex 之后的第二个参数是 amount 所以基本上第一个项目应该是顺序索引最低的项目 并且需要按数量排序 re
  • 具有路径变量的多个请求映射值

    RequestMapping value abcd employees value id public String getEmployees PathVariable value value String val PathVariable
  • PHPUnit @dataProvider 根本不起作用

    我已阅读有关该主题的文档 并且我的代码遵循数据提供程序实现的所有要求 首先 这是测试的完整代码 http pastebin com tuT9pV9h以防万一它是相关的 这是实现数据提供者的函数 Test the createGroup fu
  • Webpack 在 SSR 构建期间挂起 - Angular 12

    我正在运行 webpack v5 50 用于 Angular 项目的服务器端渲染构建 配置如下 module exports mode none entry server server ts externals dist server ma
  • 无法读取响应输出中的 application/json 消息

    我正在测试 REST API 当我进行 GET 调用来检索资源时 它会导致 500 内部服务器错误 并且在输出中返回具有媒体类型的消息application json messageType Some error type messageT
  • 调试使用 Vista API 中的 FileDialog 的 Visual Studio 2010 解决方案时出现问题

    我有一个 WinForms C Visual Studio 2008 NET 3 5 解决方案 需要升级到 Visual Studio 2010 NET 保持版本 3 5 该解决方案利用 Vista API 中的 FileDialog 有两
  • Recyclerview DiffUtil 项目更新

    我的回收视图中有无限滚动 因此 当有新数据时它会更新 我正在使用 DiffUtil 更新回收器视图中的数据 DiffUtil 确实会更新数据 但每当有更新数据时 recyclerview 就会滚动到顶部 看起来就像 使用 notificat
  • 为什么 Files.list() 并行流的执行速度比使用 Collection.parallelStream() 慢得多?

    以下代码片段是获取目录列表 对每个文件调用提取方法并将生成的药物对象序列化为 xml 的方法的一部分 try Stream