我无法理解为什么使用Stream api在同一个数组上多次迭代 导致这样的表现!
请参阅下面的代码。
public class WhyIsDifferent {
public static void main(String[] args) {
int[] values = getArray();
Iterate(values, 598, 600); // 70 ms
Iterate(values, 200, 202); // 0 ms
Iterate(values, 700, 702); // 0 ms
Iterate(values, 300, 310); // 1 ms
}
public static void Iterate(int[] values, int from, int to) {
long start = System.currentTimeMillis();
IntStream.of(values).filter(i -> i < to && i > from)
.forEach(i ->
System.out.println(i) // do a something
);
System.out.println("Time:" + (System.currentTimeMillis() - start));
}
public static int[] getArray() {
int[] values = new int[1000];
for (int i = 0; i < 1000; i++) {
values[i] = i;
}
return values;
}
}
肯定JVM优化了代码,但我不知道这是怎么回事?太神奇了! 你知道为什么会这样吗?
-
我正在测试Ubuntu 14.04 / / Oracle jdk / intel cpu。
答案 0 :(得分:10)
它不是JIT编译器。这些70毫秒中的大部分花费在整个lambda子系统的初始化上(该逻辑的入口点可能是LambdaMetaFactory
类),并且在lambda bootstrap调用(连接)上花费了很多时间。 阶段,如用户fge所述)。查看此方法,与您的方法相同,但分别测量所有步骤(并使用nanoTime
):
public static void Iterate(int[] values, int from, int to) {
long start = System.nanoTime();
final IntPredicate predicate = i -> i < to && i > from;
System.out.println("Predicate lambda creation time:" + NANOSECONDS.toMillis(System.nanoTime() - start));
start = System.nanoTime();
final IntConsumer action = System.out::println;
System.out.println("Action lambda creation time:" + NANOSECONDS.toMillis(System.nanoTime() - start));
start = System.nanoTime();
final IntStream stream = IntStream.of(values).filter(predicate);
System.out.println("Stream creation time:" + NANOSECONDS.toMillis(System.nanoTime() - start));
start = System.nanoTime();
stream.forEach(action);
System.out.println("Stream consumption time:" + NANOSECONDS.toMillis(System.nanoTime() - start));
}
这是我机器上印刷的内容:
Predicate lambda creation time:53
Action lambda creation time:2
Stream creation time:2
599
Stream consumption time:1
Predicate lambda creation time:0
Action lambda creation time:0
Stream creation time:0
201
... all timings zero from here on...
您可以看到第一次调用的整个开销是在lambda创建部分(仅在第一次运行时,包括常规初始化和链接),并且流创建也需要一些时间。在所有情况下,实际流消耗都是零时间。
使用当前版本的HotSpot,这个效果绝对值得您关注:lambda bootstrap是一件昂贵的事情。
最后注意事项:如果您重新排序lambda创建语句,您将看到大部分时间都保留在第一个 lambda中。这表明它实际上只是第一次整体创建一个承担大部分初始化成本的lambda。