for (int i=0; i<arr.length; i++) {
}
这将产生一个代码:
getstatic #4;
arraylength
以下代码:
int length = arr.length;
for (int i=0; i<length; i++) {
}
将编译为:
iload_3
两个片段之间有区别吗?哪个代码运行得更快?
如您所见,在我的情况下,数组是静态成员。静态和最终确切。考虑到JIT优化,基本优化器可以感知到并将数组的长度硬编码到方法的机器代码中。使用局部变量(第二种情况)遵循这种逻辑要困难得多,因此人们会认为第一种优化的可能性大于第二种情况。
答案 0 :(得分:7)
因为它是静态的和最终的,我怀疑可以硬编码长度,虽然我不确定它会走得那么远。但JIT编译器仍然可以使用第一种形式而不是第二种形式做得更好。
特别是,如果它可以检测到数组在循环内没有变化,它可以避免多次评估长度和删除循环内的数组边界检查 - 它可以验证你永远不会访问[0, length)
范围之外的数组。
我希望到现在为止,体面的JIT也会注意到第二种形式 - 但我仍然更喜欢第一种形式的可读性,而且我想要证据在改变那个之前,它的表现不如第二个。
与以往一样,首先编写最易读的代码,但要根据性能要求进行衡量。
答案 1 :(得分:1)
两个片段之间有区别吗?
这取决于。如果arr
变量在循环体中更新,那么这两个代码片段在语义上是不同的。
哪个代码运行得更快?
不可能说。它取决于JIT编译器生成的本机代码,并且可以从一个补丁版本到下一个补丁版本不同。唯一可以肯定的是转储本机代码并详细检查它,或者对代码进行基准测试。但无论哪种方式,差异通常都太小而不值得担心。
答案 2 :(得分:1)
JVM做的一个优化是避免在每次访问时检查数组的边界。相反,它可以限制检查第一个和最后一个值。
然而,有些微优化可能会使JVM混淆,最终你会得到速度较慢的优化代码。
我在微优化时使用的表单
for (int i = 0, length = methodCall(arr); i < length; i++) {
// use the array.
}
我更喜欢使用最简单,最明显的问题解决方案,因为JVM最有可能最好地优化这个用例。