gpt4 book ai didi

Java 8 并行处理和 Lambda 表达式

转载 作者:行者123 更新时间:2023-11-30 07:09:47 26 4
gpt4 key购买 nike

我想测试 Java8 并行流的速度有多快,所以我编写了一个程序。该程序计算给定数字列表中素数的数量。该程序以这些方式计算质数:

  1. 使用for循环;
  2. 使用 lambda 表达式;
  3. 通过使用 lambda 表达式(并行流)。

在执行程序之前,我期望并行流版本应该更快。但是结果是

在 4237 英里秒内找不到总质数 664579 ----for 循环版本
在 2440 英里秒内未找到总质数 664579 ----并行流
在 2166 英里秒内找不到总素数 664579 ----lambda 表达式

我的疑问是为什么并行版本比 lambda 版本慢

List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
numbers.add(i);
}
Stopwatch stopwatch = Stopwatch.createStarted();
int counter = 0;
for (int number : numbers) {
if (isPrime(number)) {
counter++;
}
}
stopwatch.stop();
System.out.println("Total prime no found " + counter + " in " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " mili sec");

stopwatch = Stopwatch.createStarted();
long count1 = numbers.parallelStream().filter(n -> isPrime(n)).count();
stopwatch.stop();
System.out.println("Total prime no found " + count1 + " in " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " mili sec");

stopwatch = Stopwatch.createStarted();
long count2 = numbers.stream().filter(n -> isPrime(n)).count();
System.out.println("Total prime no found " + count2 + " in " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " mili sec");
stopwatch.stop();

上述程序使用 google Guava 库来计算耗时。

最佳答案

问题很可能是在测试期间 JIT 编译器(重新)编译代码。这意味着您的比较是不公平的,因为较晚的测试受益于较早测试引起的编译。

这是微基准测试经常犯的错误。有很多文章解释了这个问题,例如Robust Java benchmarking .如果我先添加一些代码来预热 JIT,结果是预期的。我的主要方法如下所示:

public static void main(String... args) {
System.out.println("Warmup...");
for (int i = 0; i < 5000; ++i) {
run(Demo::testLoop, 5000);
run(Demo::testStream, 5000);
run(Demo::testParallel, 5000);
}
System.out.println("Benchmark...");
int bound = 10000000;
System.out.printf("Loop: %s\n", run(Demo::testLoop, bound));
System.out.printf("Stream: %s\n", run(Demo::testStream, bound));
System.out.printf("Parallel: %s\n", run(Demo::testParallel, bound));
}

输出如下:

Loop:     7.06s (664579)
Stream: 7.06s (664579)
Parallel: 3.84s (664579)

如果您传递选项 -XX:+PrintCompilation到 Java VM,您可以看到 JIT 何时何地启动,并且现在几乎所有编译都发生在预热阶段。

请注意,并行流并不是这种并行化的最佳解决方案,因为 isPrime 的复杂度取决于值。这意味着,序列的前半部分比后半部分(依此类推)需要的工作要少得多。

作为引用,以下是我实现的其余方法:

public static boolean isPrime(int value) {
for (int i = 2; i * i <= value; ++i)
if (value % i == 0) return false;
return true;
}

public static long testLoop(int bound) {
long count = 0;
for (int i = 2; i < bound; ++i)
if (isPrime(i)) ++count;
return count;
}

public static long testStream(int bound) {
return IntStream.range(2, bound).filter(Demo::isPrime).count();
}

public static long testParallel(int bound) {
return IntStream.range(2, bound).parallel().filter(Demo::isPrime).count();
}

public static String run(IntToLongFunction operation, int bound) {
long start = System.currentTimeMillis();
long count = operation.applyAsLong(bound);
long millis = System.currentTimeMillis() - start;
return String.format("%4.2fs (%d)", millis / 1000.0, count);
}

关于Java 8 并行处理和 Lambda 表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22703838/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com