Java 8 Stream的正确配置是什么

时间:2017-03-30 22:48:18

标签: java parallel-processing java-stream

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

  1. 串行
  2. 并行流
  3. 使用ForkJoinPool
  4. 令人惊讶的是,Serial方法是最快的方法。实际上,其他两个时间需要%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();
        }
    }
    

    由于

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

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

  1. 按照流的步骤进行数据库/网络或其他IO调用
  2. CPU计算量大(例如总和超过10K整数。添加数十或数百个整数并不是一个繁重的计算)。
  3. 就我个人而言,我认为并行流过于强调日常编码。