【问题标题】:What is the right configuration for Java 8 StreamJava 8 Stream 的正确配置是什么
【发布时间】:2017-03-30 22:48:18
【问题描述】:

我有以下代码(只是我为这个问题写的一个例子),它只是计算一个范围的总和 我通过三种方式实现它:

  1. 序列号
  2. 并行流
  3. 使用 ForkJoinPool

令人惊讶的是,串行方法是最快的方法。事实上,它花费了其他两个时间的 %10

为了使其更快,Java Stream 的正确配置是什么? 我做错了什么?

package ned.main;

import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.IntStream;

public class TestParallelStream {

    static private void testParallelStream() {
        System.setProperty("java.util.concurrent.ForkJoinPool.common‌​.parallelism", "1000000");

        ForkJoinPool forkJoinPool = new ForkJoinPool(10000);

        Date start = new Date();

        long sum1 = 0;
        for (int i = 0; i < 1_000_000; ++i) {
            sum1 += i * 10;
        }

        Date start1 = new Date();

        long sum2 = IntStream.range(1, 1_000_000)
                        .parallel()
                        .map(x -> x * 10)
                        .sum();

        Date start2 = new Date();

        try {
            long sum3 = forkJoinPool.submit(() -> 
                IntStream
                    .range(1, 1_000_000)
                    .parallel()
                    .map(x -> x * 10)
                    .sum())
                        .get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        long serial = start1.getTime() - start.getTime();
        long parallelstream = start2.getTime() - start1.getTime();
        long withfork = start2.getTime() - start1.getTime();

        System.out.println(serial + "," + parallelstream + "," + withfork);

    }

    public static void main(String[] args) {
        testParallelStream();
    }
}

谢谢

【问题讨论】:

  • jvm 设置流/线程所需的时间比累积乘法要长得多。尝试使用更多的复杂性,数字会改变
  • 感谢 cmets。我同意给出的示例程序可能是您提到的情况。但是如何正确配置并行方法以及如何确保获得最佳性能?在我的实际应用中,我有数千个向量,我需要找到最接近给定向量的向量。这更复杂,但仍然表明帽子序列更快

标签: java parallel-processing java-stream


【解决方案1】:

似乎对并行属性有根本性的错误理解。要利用所有 CPU 内核进行计算,并行度应该与内核数量相匹配,这已经是默认设置。

将并行度设置为 1000000 是没有意义的——即使在不太可能的情况下,您确实有 1000000 个处理器,在这种情况下,设置已经是默认值的设置仍然过时。附带说明一下,如果您有 1000000 个处理单元,那么包含 1000000 个乘法的作业将太小而无法从该硬件中受益。您为每个 int 乘法启动一个线程,这太疯狂了。

如果有疑问,请不要乱用该设置并将并行度保留为默认值。

还是要看实际操作,是否会受益于并行处理。 JVM 的优化器只会处理小块的顺序代码,因此将操作拆分为并行处理的块可能会降低代码优化的好处。

在最极端的变体中,形式为循环

long sum1 = 0;
for(int i=from; i<to; ++i) sum1 += i * constant;

可以优化为

long sum1=((long)from+to-1)*(to-from)/2  * constant;

这将导致任意范围的计算时间恒定,因此将范围分成多个子范围,并行计算,通常不能缩短所需时间。但这当然是 JVM 特定的。

在 HotSpot 的情况下,它有一些非常严格的内联阈值,使用流代码执行操作可能会超过阈值,从而降低 JVM 的优化潜力。是否发生这种情况,也可以通过对等效的顺序流操作进行基准测试来测试。在最好的情况下,它应该像循环一样执行。如果没有,您知道流操作将比循环带来缺点,该循环也适用于并行流。调整 JVM 选项可能会有所帮助(希望将来默认值会变得更加“流友好”)。

【讨论】:

    【解决方案2】:

    根据我的个人经验,串行流是 99% 任务的最佳选择,与并行流相比。 这是 Doug Lea 关于When to use parallel streams 的文章。基本上,当且仅当您遇到性能问题时才考虑使用并行流。有一些提示:

    1. 分步进行 db/network 或其他 IO 调用
    2. CPU 繁重的计算(例如,求和超过 10K 个整数。添加数十或数百个整数并不是繁重的计算)。

    就个人而言,我认为并行流在日常编码中被过分强调了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-24
      • 1970-01-01
      • 1970-01-01
      • 2017-01-08
      • 1970-01-01
      • 2017-03-08
      • 2013-09-13
      相关资源
      最近更新 更多