具有无限系列的Java Stream-API性能

时间:2014-06-02 21:35:52

标签: java performance lambda java-8 java-stream

我观察到Java8和新Stream-API的一些特殊行为。

我希望以下两个陈述的表现相同,但事实并非如此。

LongStream.iterate(1, n -> n + 1).limit(5000)
          .anyMatch(n -> isPerfectCube((n*n*n)+((n*n)*p)));

LongStream.iterate(1, n -> n + 1)
          .anyMatch(n -> isPerfectCube((n*n*n)+((n*n)*p)));

这两个语句都应该返回true,并且我不希望任何性能差异,因为它们可以在找到的第一个匹配项上发生短路。这些陈述的唯一区别是,一个是在要迭代的数字范围的上限,而另一个不是。

有人可以向我解释为什么一个人会比另一个人跑得更快并且使用更少的内存?

1 个答案:

答案 0 :(得分:7)

p有一些值,其中条件适用于n的大值。例如p = 3n = 50_331_648的条件成立。在这种情况下,5000的限制当然会在性能方面获胜,但两次计算不会返回相同的结果。

我随机选择了一个p(3002),对于小于5000的n返回true,结果非常接近(尽管带limit的版本稍慢,可能是因为额外条件n < 5000)。

基准测试结果(每次调用anyMatch时的微秒数):

Benchmark                    Mode   Samples         Mean   Mean error    Units
c.a.p.SO24003674.limit       avgt         5      130.165        2.663    us/op
c.a.p.SO24003674.noLimit     avgt         5      126.876        2.440    us/op

基准代码(使用jmh):

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Fork(1)
public class SO24003674 {

  private int p = 3002;

  @GenerateMicroBenchmark
  public boolean limit() {
    return LongStream.iterate(1, n -> n + 1).limit(5000)
              .anyMatch(n -> isPerfectCube((n * n * n) + ((n * n) * p)));
  }

  @GenerateMicroBenchmark
  public boolean noLimit() {
    return LongStream.iterate(1, n -> n + 1)
              .anyMatch(n -> isPerfectCube((n * n * n) + ((n * n) * p)));
  }

  private static boolean isPerfectCube(long n) {
    long tst = (long) (Math.cbrt(n) + 0.5);
    return tst * tst * tst == n;
  }
}