如果在 java 并行流上,forEach 被 n 个线程调用,这是否意味着一次读取 n 个项目进行处理?
If on a java parallel stream forEach is called by n threads does it mean that at a time n items are read for processing?
所以,我想并行地逐行处理一个文件,由于每行处理都是独立的,所以我不需要按顺序处理。文件也很大,所以我不想一开始就将整个文件读入内存。
所以我在想如果我调用 java NIO File.lines()
然后使用 n 个线程处理结果流。是不是说只有n行会被读入内存处理呢?
我在想这种方法应该类似于桶处理方法,其中线程将 n 行读入阻塞队列,而线程池并行处理这些读取的行。
最重要的信息优先; Files.lines()
返回的流没有将整个文件加载到内存中。
同时保存在内存中的数据量取决于实现,并且在打开并行处理时不可避免地会增加,但它不取决于文件大小(除非它是一个相当小的文件)。
您可以简单地尝试它是否适合您的情况并忽略下面的细节。
在 Java8 的参考实现中,Files.lines()
只是委托给 BufferedReader.lines()
。唯一的区别是安装了关闭处理程序,因此关闭流将关闭底层 BufferedReader
。因此涉及两个缓冲区,一个用于字符集转换的源数据,另一个位于 BufferedReader
内,用于在创建行字符串并将它们传递给流之前识别行边界。
由于这些缓冲区的大小不依赖于文件的大小,因此在处理大文件时无需担心。
但是正如 Reader#lines() parallelizes badly due to nonconfigurable batch size policy in its spliterator Java 中所讨论的那样,8 对这个流的实现不太适合并行处理。它继承了后备拆分策略,该策略至少缓冲 1024 个元素,并在每次拆分操作时将此块大小增加 1024,每个块最多 33554432 个元素。在行流的情况下,这些是内存中一次可能存在的行数,每个线程。根据“大文件”对您的意义,它可能归结为在最坏的情况下将整个文件放在内存中,或者不是整个文件,但对于您的用例来说仍然太多了。
此问题已由 JDK-8072773, (fs) Files.lines needs a better splitting implementation for stream source 为 JDK 9 解决。但值得注意的是,该请求已按字面意思 实施。
而不是趁机改进BufferedReader.lines()
直接实现一个Spliterator
,大部分代码根本就没动过。相反,Files.lines
只是获得了更好的拆分实现,否则会通过相同的低效代码路径运行。
如果满足前提条件(Path
在默认文件系统上,字符集是支持的字符集之一,文件可以映射为单个 ByteBuffer
),一个特殊的拆分器创建它进行内存映射并识别 ByteBuffer
中的潜在拆分位置,即换行符。但是一旦初始拆分完成,尽管有一个 ByteBuffer
代表块并且知道如何识别其中的换行符,代码会为块创建一个新的 BufferedReader
,以进行同样低效的解码并像以前一样通过包装 Iterator
进行流式传输,但现在针对每一块工作。
所以虽然这是一个改进,但它有以下缺点:
虽然它是专门针对 大 文件的优化,但它对大于 2 GiB
的文件不再有效
它仅适用于默认文件系统和某些字符集(当前为 UTF-8、ISO-LATIN-1 和 US-ASCII)
如果工作负载不平衡,例如你有过滤器匹配文件的一半,然后是实际的昂贵工作,所以初始拆分不足以给每个 CPU 核心一些工作,并行性能会受到影响,因为新的拆分器不支持拆分遍历开始后,甚至 BufferedReader
的缓冲区数组变体也不行
虽然原来的方法只有一个 BufferedReader
和上面提到的两个缓冲区,但现在每个工作块都有一个 BufferedReader
。
还是如开头所说,Files.lines()
并没有把整个文件加载到内存中。您的用例可能会从这些改进中受益。不过要看具体情况。
所以,我想并行地逐行处理一个文件,由于每行处理都是独立的,所以我不需要按顺序处理。文件也很大,所以我不想一开始就将整个文件读入内存。
所以我在想如果我调用 java NIO File.lines()
然后使用 n 个线程处理结果流。是不是说只有n行会被读入内存处理呢?
我在想这种方法应该类似于桶处理方法,其中线程将 n 行读入阻塞队列,而线程池并行处理这些读取的行。
最重要的信息优先; Files.lines()
返回的流没有将整个文件加载到内存中。
同时保存在内存中的数据量取决于实现,并且在打开并行处理时不可避免地会增加,但它不取决于文件大小(除非它是一个相当小的文件)。
您可以简单地尝试它是否适合您的情况并忽略下面的细节。
在 Java8 的参考实现中,Files.lines()
只是委托给 BufferedReader.lines()
。唯一的区别是安装了关闭处理程序,因此关闭流将关闭底层 BufferedReader
。因此涉及两个缓冲区,一个用于字符集转换的源数据,另一个位于 BufferedReader
内,用于在创建行字符串并将它们传递给流之前识别行边界。
由于这些缓冲区的大小不依赖于文件的大小,因此在处理大文件时无需担心。
但是正如 Reader#lines() parallelizes badly due to nonconfigurable batch size policy in its spliterator Java 中所讨论的那样,8 对这个流的实现不太适合并行处理。它继承了后备拆分策略,该策略至少缓冲 1024 个元素,并在每次拆分操作时将此块大小增加 1024,每个块最多 33554432 个元素。在行流的情况下,这些是内存中一次可能存在的行数,每个线程。根据“大文件”对您的意义,它可能归结为在最坏的情况下将整个文件放在内存中,或者不是整个文件,但对于您的用例来说仍然太多了。
此问题已由 JDK-8072773, (fs) Files.lines needs a better splitting implementation for stream source 为 JDK 9 解决。但值得注意的是,该请求已按字面意思 实施。
而不是趁机改进BufferedReader.lines()
直接实现一个Spliterator
,大部分代码根本就没动过。相反,Files.lines
只是获得了更好的拆分实现,否则会通过相同的低效代码路径运行。
如果满足前提条件(Path
在默认文件系统上,字符集是支持的字符集之一,文件可以映射为单个 ByteBuffer
),一个特殊的拆分器创建它进行内存映射并识别 ByteBuffer
中的潜在拆分位置,即换行符。但是一旦初始拆分完成,尽管有一个 ByteBuffer
代表块并且知道如何识别其中的换行符,代码会为块创建一个新的 BufferedReader
,以进行同样低效的解码并像以前一样通过包装 Iterator
进行流式传输,但现在针对每一块工作。
所以虽然这是一个改进,但它有以下缺点:
虽然它是专门针对 大 文件的优化,但它对大于 2 GiB
的文件不再有效它仅适用于默认文件系统和某些字符集(当前为 UTF-8、ISO-LATIN-1 和 US-ASCII)
如果工作负载不平衡,例如你有过滤器匹配文件的一半,然后是实际的昂贵工作,所以初始拆分不足以给每个 CPU 核心一些工作,并行性能会受到影响,因为新的拆分器不支持拆分遍历开始后,甚至
的缓冲区数组变体也不行BufferedReader
虽然原来的方法只有一个
BufferedReader
和上面提到的两个缓冲区,但现在每个工作块都有一个BufferedReader
。
还是如开头所说,Files.lines()
并没有把整个文件加载到内存中。您的用例可能会从这些改进中受益。不过要看具体情况。