繁体   English   中英

为什么并行流比顺序流慢?

[英]Why is a parallel stream slower than a sequential stream?

我有两个函数可以解决相同的问题。 第一个使用顺序流,第二个使用并行流。

public static int digitalRoot(int n) {
    int sum = String.valueOf(n).chars().map(i -> Integer.parseInt(String.valueOf((char) i))).sum();
    if (sum >= 10) {
        return digitalRoot(sum);
    } else {
        return sum;
    }
}

public static int digitalRootParallel(int n) {
    int sum = String.valueOf(n).chars().parallel().
            map(i -> Integer.parseInt(String.valueOf((char) i))).sum();
    if (sum >= 10) {
        return digitalRootParallel(sum);
    } else {
        return sum;
    }
}

我一次执行了这些功能。 并行函数digitalRootParallel()比顺序函数(32 ms)要快(5 ms

但是,当我在1.000.000的循环中执行它们时,顺序(117毫秒)比并行(1124毫秒)要快。

    for (int i = 0; i < 1000000; i++) {
        sum = digitalRoot(n);
    }

为什么并行流的循环变慢?

我能分享的是令我惊讶的是,除了并行版本的速度下降之外,您从未测量过任何东西。 您几乎不需要做任何事情Fork / Join:解析和加起来最多十位数。 F / J可能会决定甚至不值得对其进行拆分,并且将在单个线程上执行整个计算,但是这样做所涉及的开销将扼杀性能。

如果希望看到并行化的任何好处,请确保至少有半秒的顺序计算价值,并且可以轻松地将其拆分为子任务。

首先,您的代码…有点复杂。 代替

String.valueOf(n).chars().map(i -> Integer.parseInt(String.valueOf((char) i))).sum();

你可以写

String.valueOf(n).chars().map(i -> i-'0').sum();

但是,即使使用原始代码,操作也是如此简单,HotSpot优化器可以将其转换为较短的代码,以使其执行速度比任何新线程启动或同步都快。

这尤其适用于基准测试,在该基准测试中您多次执行操作而不实际使用其结果。 在纯顺序上下文中,优化器可以识别出未使用结果,并且在没有任何副作用的情况下忽略了整个操作。 相反,并行执行或更抽象的与其他线程进行通信的代码具有副作用。

因此,优化器无法对跨越多个线程的操作执行相同的操作。 Stream实现也无法识别操作的简便性。 如果您请求并行执行,则只要您知道自己在做什么, Stream实现就会尊重它。


附加说明:

当前 String.chars()实现的一个有趣的特性是,它创建了一个PrimitiveIterator.OfInt ,它将被包装在Spliterator.OfInt ,而不是直接实现Spliterator.OfInt 这意味着没有直接拆分功能,因为PrimitiveIterator.OfInt不提供此功能。 取而代之的是,拆分将通过迭代一个或多个元素,然后将它们复制到一个数组中并提供该数组以供其他线程处理来执行。

但是迭代和复制到临时数组中可能要比顺序执行中的实际操作花费更多时间。

这些不良的拆分功能是一个已知问题 ,将在Java 9中解决。但是,您的操作仍不太可能从并行执行中受益。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM