int和long的迭代速度对比

8
我有以下两个程序:
long startTime = System.currentTimeMillis();
for (int i = 0; i < N; i++);
long endTime = System.currentTimeMillis();
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs");

并且

long startTime = System.currentTimeMillis();
for (long i = 0; i < N; i++);
long endTime = System.currentTimeMillis();
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs");

注意:唯一的区别是循环变量的类型(int和long)。
当我运行这个程序时,第一个程序始终在0到16毫秒之间打印输出,无论N的值为何。第二个程序需要更长时间。对于N == Integer.MAX_VALUE,它在我的机器上大约需要1800毫秒才能运行。运行时间似乎与N呈线性关系。
那么这是为什么呢?
我想JIT编译器会优化int循环。有充分的理由,因为显然它什么也不做。但为什么它不对long循环进行优化呢?
一位同事认为我们可能在long循环中测量JIT编译器正在执行其工作,但由于运行时间似乎与N呈线性关系,这可能并不是真正的原因。
我使用的是JDK 1.6.0更新17版本。
C:\>java -version
java version "1.6.0_17"
Java(TM) SE Runtime Environment (build 1.6.0_17-b04)
Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01, mixed mode)

我正在使用Windows XP Professional x64 Edition,Service Pack 2,配备2.40GHz的Intel Core2 Quad CPU。
免责声明 我知道微基准测试在生产中没有用处。我也知道System.currentTimeMillis()并不像它的名字所暗示的那样精确。这只是我在瞎搞时注意到的一些事情,我只是好奇为什么会发生这种情况;仅此而已。

我完全同意@Andrzej的观点,但如果只是出于好奇心,您可以使用PrintAssembly插件来查看生成的代码:http://wikis.sun.com/display/HotSpotInternals/PrintAssembly - Joachim Sauer
如果你使用 -Xint 标志运行会发生什么?这将防止 Hotspot 编译,因此您将获得更好的比较。 - Steve McLeod
@Steven:但是使用“-Xint”选项,对于任何类似实际用途的东西来说,结果甚至更加无意义。 - Joachim Sauer
我很好奇PrintAssembly会显示什么,如果有人想运行它并发布结果... - matt b
4个回答

5
这是一个有趣的问题,但老实说我并不认为考虑Hotspot在这里的行为会产生有用的信息。你得到的任何答案都不能在一般情况下转移(因为我们正在研究Hotspot在一个特定情况下执行的优化),所以它们将帮助你理解为什么一个空操作比另一个更快,但它们不会帮助你编写更快的“真实”程序
写关于这种事情的具有误导性的微型基准测试非常容易--请参见IBM DW文章了解一些常见的陷阱,如何避免它们以及对你正在做的事情的一些一般评论。
所以,实际上这是一个“无可奉告”的回答,但我认为这是唯一有效的回应。一个编译时微不足道的空操作循环并不需要快速,所以编译器在某些情况下没有被优化为快速。

当然,你是对的。我问这个问题纯粹是因为好奇心,我在摆弄时注意到了一些东西;我并不打算在真正的程序中这样做。 :) - jqno

4
您可能正在使用32位的JVM。如果使用64位的JVM,结果可能会有所不同。在32位的JVM中,int可以映射到本地的32位整数,并且可以通过一次操作进行增加。但是对于long类型来说情况并非如此,增加时需要更多的操作。
关于int和long类型大小的讨论,请查看此链接

好的观点。不过,我正在使用64位JVM。我会更新问题。 - jqno
将操作系统和硬件配置的一些细节(主要是CPU)发布出来也会很有用。 - kgiannakakis

3

我猜测 - 只是猜测 - 是这样的:

JVM得出结论,第一个循环实际上什么也没做,因此将其完全删除。没有变量“逃逸”出for循环。

在第二种情况下,循环也什么也没做。但可能是JVM代码确定循环什么也不做时有一个“if (i的类型)== int”的子句。在这种情况下,消除无用的for循环的优化仅适用于int。

删除代码的优化必须确保没有副作用。 JVM编码人员似乎在谨慎方面犯了错误。


1

像这样的微基准测试并没有太多意义,因为结果很大程度上取决于Hotspot JIT的内部工作方式。

此外,请注意,使用System.currentTimeMillis()获取的系统时钟值在所有操作系统上都没有1毫秒的分辨率。您不能使用它来非常精确地计时非常短的持续时间事件。

请查看本文,了解为什么在Java中进行微基准测试并不像大多数人想象的那样容易:一个有缺陷的微基准测试的剖析


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接