我观察到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
,并且我不希望任何性能差异,因为它们可以在找到的第一个匹配项上发生短路。这些陈述的唯一区别是,一个是在要迭代的数字范围的上限,而另一个不是。
有人可以向我解释为什么一个人会比另一个人跑得更快并且使用更少的内存?
答案 0 :(得分:7)
p
有一些值,其中条件适用于n
的大值。例如p = 3
,n = 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;
}
}