【发布时间】:2020-10-28 23:26:38
【问题描述】:
我决定在 Java 1.8.045,CPU:AMD a10-6800k 4-Cores x64 上测量常规流和并行流之间的性能差异:
如果我将并行流放在顺序流之前,它的工作速度比顺序流慢。
我怀疑基础代码中的第一个流无论如何都会运行缓慢。
我认为问题在于 JVM 启动需要时间。我什至将 Thread.sleep(100000) 放在基本代码的开头。它没有帮助。
如果我放置第一个顺序流,第二个是并行流 - 一切看起来都正确,但是 ... :)
计划测试并行流与常规流:加载 6M 文本文件,计算大小超过 12 个符号的单词数量。
我通过两个分开的流来完成它,并测量每个流的性能。
排除任何不必要的影响:
- 我将文件两次加载到两个单独的 ArrayLists(特定流的每个数组列表)。
- 我通过旧的 Java 方法加载数据以排除来自这方面的任何影响。
- 我什至将字符串放入字符串池中。
- 我提出了 -Xms 和 -Xmx :VM 选项:
-verbose:gc -Xms1000000k -Xmx3000000k
我不明白:[GC(分配失败)65536K->9238K(251392K),0.0085762 秒]
如果我先放并行流,我得到:
并行流持续时间:287.271699
并行流的字数:7588
常规流时长:138.9853
按常规流计算的字数:7588
并行流时长几乎是常规流时长的 2 倍。
如果我先放常规流,我会得到:
常规流持续时间:336.4724
按常规流计算的字数:7588
并行流时长:98.5675
并行流的字数:7588
我知道 JVM 可能会启动它应该很热,我应该测量几次。 是的,如果我将第一个 Stream 放入循环并在同一过程中进行多次计算,第一次计算很慢,第二次计算更快。 我想在 JVM 启动时期望并行流的稳定行为也是正常的。 GC 对应用程序启动时的性能是否有其他影响? 为什么会发生?我们使用不同的集合。为什么基础代码中的第一个流计算总是很慢?
我的基本代码:
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.regex.Pattern;
public class StreamsExperiment {
public static void main(String[] args) throws InterruptedException {
try(BufferedReader bufferedReaderParallel = new BufferedReader(new FileReader("resource//war_and_peace.txt"))){
String line = bufferedReaderParallel.readLine();
ArrayList<String> lineListParallel = new ArrayList<>();
while (line != null) {
line.intern();
lineListParallel.add(line);
line = bufferedReaderParallel.readLine();
}
long startTimeParallel = System.nanoTime();
long bigWordsCount2 = lineListParallel.stream().parallel().flatMap(x -> Pattern.compile("\\PL+").splitAsStream(x)).filter(w -> w.length() > 12).count();
long stopTimeParallel = System.nanoTime();
System.out.println("Parallel stream duration: " + (stopTimeParallel - startTimeParallel) / 1000000.0);
System.out.println("Words count by parallel stream : " + bigWordsCount2);
} catch (IOException e){
System.out.println("I/O error : " + e);
}
try(BufferedReader bufferedReaderRegular = new BufferedReader(new FileReader("resource//war_and_peace.txt"))){
String line = bufferedReaderRegular.readLine();
ArrayList<String> lineListRegular = new ArrayList<>();
while (line != null) {
line.intern();
lineListRegular.add(line);
line = bufferedReaderRegular.readLine();
}
long startTimeRegular = System.nanoTime();
long bigWordsCount1 = lineListRegular.stream().flatMap(x -> Pattern.compile("\\PL+").splitAsStream(x)).filter(w -> w.length() > 12).count();
long stopTimeRegular = System.nanoTime();
System.out.println("Regular stream duration: " + (stopTimeRegular - startTimeRegular) / 1000000.0);
System.out.println("Words count by regular stream : " + bigWordsCount1);
} catch (IOException e){
System.out.println("I/O error : " + e);
}
}
}
【问题讨论】:
-
与问题无关,但不应该将
Pattern.compile("\\PL+")定义为变量吗?为每条记录编译它也可能会对性能产生一些影响。 -
当您尝试多次运行这些代码块 (10+) 时会发生什么?并尝试交替运行它们。
-
这不是衡量性能的方式。请看Microbenchmarking with JMH
-
为了证明你的“基准”是多么荒谬,只需交换两个测试,先做顺序,然后再做并行。除此之外,我建议了解
Files.readAllLines(…)ant,好吧,既然阅读不是你的基准测试的一部分,你为什么要这样做两次而不是重复使用列表?而line.intern();调用毫无意义,它白白浪费了 CPU 周期。 -
既然你已经注意到自己总是第一个流操作比第二个慢,不清楚你为什么还在问parallel 流。在您的
main方法之前,甚至没有加载 Stream API 类,更不用说初始化、编译和/或优化了。也有可能之前没有使用过正则表达式类。因此,您正在测量加载、初始化和编译/优化大量类所需的时间。
标签: java performance parallel-processing java-stream startup