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


序列号
并行流
与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();
    }
}


谢谢

最佳答案

似乎对并行性有根本的错误理解。要利用所有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选项可能会有所帮助(希望默认值将来会变得更加“流友好”)。

09-25 15:57