为什么并行流速较慢?

Sin*_*ani 4 java java-8 java-stream

我正在玩无限流,并制作了这个程序进行基准测试.基本上,您提供的数字越大,完成的速度就越快.但是,我惊讶地发现,与顺序流相比,使用parellel流导致性能呈指数级下降.直觉上,人们会期望在多线程环境中生成和评估无限的随机数流,但事实并非如此.为什么是这样?

    final int target = Integer.parseInt(args[0]);
    if (target <= 0) {
        System.err.println("Target must be between 1 and 2147483647");
        return;
    }

    final long startTime, endTime;
    startTime = System.currentTimeMillis();

    System.out.println(
        IntStream.generate(() -> new Double(Math.random()*2147483647).intValue())
        //.parallel()
        .filter(i -> i <= target)
        .findFirst()
        .getAsInt()
    );

    endTime = System.currentTimeMillis();
    System.out.println("Execution time: "+(endTime-startTime)+" ms");
Run Code Online (Sandbox Code Playgroud)

Lac*_*lev 6

我完全赞同其他的评论和答案,但实际上,如果目标非常低,你的测试表现得很奇怪.在我的适度笔记本电脑上,当给出非常低的目标时,并行版本平均慢约60倍.这种极端的差异无法用流API中的并行化开销来解释,所以我也很惊讶:-).IMO的罪魁祸首在于:

Math.random()
Run Code Online (Sandbox Code Playgroud)

在内部,此调用依赖于全局实例java.util.Random.在Random文档中写道:

java.util.Random的实例是线程安全的.但是,跨线程并发使用相同的java.util.Random实例可能会遇到争用并因此导致性能不佳.请考虑在多线程设计中使用ThreadLocalRandom.

所以我认为与并行执行相比,并行执行的真正糟糕的性能是由随机的线程争用而不是任何其他开销来解释的.如果您使用ThreadLocalRandom(根据文档中的建议),那么性能差异将不会那么显着.另一种选择是实施更高级的数字供应商.

  • 是的,我刚检查过它.但它确实引入了与本地随机线程相比的巨大差异,正如文档所述...:O (3认同)
  • @Sergey Fedorov:CAS 不使用等待,但 CAS 可能会失败,在这种情况下,调用代码必须重试操作(除非可以选择失败)。重复的重试就像轮询,这就是 Alex Pakka 所说的“忙等待”。在将线程置于等待状态的意义上,这不是等待。 (2认同)