2015-11-07 105 views
4

所以我读一本书对Java 8,当我看到他们的外部和内部迭代之间做比较和思考明智两相比较,性能。Java 8 - 外部迭代比内部迭代执行得更好?

我有,只是总结了整数高达n序列的方法。

迭代之一:

private static long iterativeSum(long n) { 

    long startTime = System.nanoTime(); 
    long sum = 0; 

    for(long i=1; i<=n; i++) { 
     sum+=i; 
    } 


    long endTime = System.nanoTime(); 
    System.out.println("Iterative Sum Duration: " + (endTime-startTime)/1000000); 

    return sum; 
} 

个顺序 - 使用内部迭代中

private static long sequentialSum(long n) { 

    long startTime = System.nanoTime(); 

    //long sum = LongStream.rangeClosed(1L, n) 
    long sum = Stream.iterate(1L, i -> i+1) 
      .limit(n) 
      .reduce(0L, (i,j) -> i+j); 

    long endTime = System.nanoTime(); 
    System.out.println("Sequential Sum Duration: " + (endTime-startTime)/1000000); 

    return sum; 
} 

我试图做对他们的一些基准测试,事实证明,使用外部迭代的一个执行远远好过一个使用内部迭代。

这里是我的驱动程序代码:

public static void main(String[] args) { 

    long n = 100000000L; 

    for(int i=0;i<10000;i++){ 
    iterativeSum(n); 
    sequentialSum(n); 
    } 
    iterativeSum(n); 
    sequentialSum(n); 
} 

的运行时间为Iteravtive一个总是< 50毫秒,而执行时间顺序一个总是> 250毫秒。

我无法理解为什么内部迭代中没有在这里进行外部迭代?

+4

fyi http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java你的测试没有做你期望的测试 – Zavior

+0

我试过了在循环中运行这些方法超过一千次以预热JIT并遍历所有代码路径。但是我没有看到2之间的差距缩小。迭代总是<50,顺序(内部迭代)总是> 250ms – AgentX

回答

11

即使呈现的结果是完全不相干的,观察到的效果确实发生:数据流API做了哪些对这种简单的任务不能完全即使在热身消除在实际应用中的开销。让我们写一个JMH基准:

@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) 
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) 
@BenchmarkMode(Mode.AverageTime) 
@OutputTimeUnit(TimeUnit.MICROSECONDS) 
@Fork(3) 
@State(Scope.Benchmark) 
public class IterativeSum { 
    @Param({ "100", "10000", "1000000" }) 
    private int n; 

    public static long iterativeSum(long n) { 
     long sum = 0; 

     for(long i=1; i<=n; i++) { 
      sum+=i; 
     } 
     return sum; 
    } 

    @Benchmark 
    public long is() { 
     return iterativeSum(n); 
    } 
} 

这里的基准测试:纯循环。在我的箱子,结果如下:

Benchmark    (n) Mode Cnt  Score  Error Units 
IterativeSum.is  100 avgt 30  0.074 ± 0.001 us/op 
IterativeSum.is  10000 avgt 30  6.361 ± 0.009 us/op 
IterativeSum.is 1000000 avgt 30 688.527 ± 0.910 us/op 

这是你的基于流API迭代版本:

public static long sequentialSumBoxed(long n) { 
    return Stream.iterate(1L, i -> i+1).limit(n) 
       .reduce(0L, (i,j) -> i+j); 
} 

@Benchmark 
public long ssb() { 
    return sequentialSumBoxed(n); 
} 

结果是这样的:

Benchmark    (n) Mode Cnt  Score  Error Units 
IterativeSum.ssb  100 avgt 30  1.253 ± 0.084 us/op 
IterativeSum.ssb 10000 avgt 30 134.959 ± 0.421 us/op 
IterativeSum.ssb 1000000 avgt 30 9119.422 ± 22.817 us/op 

非常失望:13-慢21倍。这个版本里面有很多装箱操作,这就是为什么创建原始流专业化的原因。让我们来看看非盒装版本:

public static long sequentialSum(long n) { 
    return LongStream.iterate(1L, i -> i+1).limit(n) 
        .reduce(0L, (i,j) -> i+j); 
} 

@Benchmark 
public long ss() { 
    return sequentialSum(n); 
} 

结果如下:

Benchmark    (n) Mode Cnt  Score  Error Units 
IterativeSum.ss  100 avgt 30  0.661 ± 0.001 us/op 
IterativeSum.ss  10000 avgt 30 67.498 ± 5.732 us/op 
IterativeSum.ss 1000000 avgt 30 1982.687 ± 38.501 us/op 
现在

好多了,但仍然比较慢2.8-10x倍。另一种方法是使用范围:

public static long rangeSum(long n) { 
    return LongStream.rangeClosed(1, n).sum(); 
} 

@Benchmark 
public long rs() { 
    return rangeSum(n); 
} 

结果如下:

Benchmark    (n) Mode Cnt  Score  Error Units 
IterativeSum.rs  100 avgt 30  0.316 ± 0.001 us/op 
IterativeSum.rs  10000 avgt 30 28.646 ± 0.065 us/op 
IterativeSum.rs 1000000 avgt 30 2158.962 ± 514.780 us/op 

现在是慢3.1-4.5x倍。这个缓慢的原因是流API具有打MaxInlineLevel JVM限制很长的调用链,因此它不能被默认完全内联。您可能会增加此限制设置像-XX:MaxInlineLevel=20并得到以下结果:

Benchmark    (n) Mode Cnt  Score  Error Units 
IterativeSum.rs  100 avgt 30  0.111 ± 0.001 us/op 
IterativeSum.rs  10000 avgt 30  9.552 ± 0.017 us/op 
IterativeSum.rs 1000000 avgt 30 729.935 ± 31.915 us/op 

好多了:现在只要1.05-1.5x慢。

该测试的问题在于迭代版本的循环体非常简单,因此可以通过JIT编译器进行高效的展开和向量化处理,而且对于复杂的Stream API代码,使用相同的效率难度更大。然而在实际应用中,你不可能在循环中总结数字(为什么不写n*(n+1)/2代替?)。存在实际问题即使使用默认值MaxInlineLevel设置,Stream API开销也要低得多。

+0

看起来很有趣,我会更多地考虑MaxInlineLevel做更多的测试,然后接受你的答案。谢谢。 – AgentX