【问题标题】:Iteration speed of int vs longint 与 long 的迭代速度
【发布时间】:2011-02-06 01:10:56
【问题描述】:

我有以下两个程序:

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");

注意:唯一的区别是循环变量的类型(intlong)。

当我运行它时,第一个程序始终在 0 到 16 毫秒之间打印,而不管 N 的值如何。第二个需要更长的时间。对于N == Integer.MAX_VALUE,它在我的机器上运行大约 1800 毫秒。 N 中的运行时间似乎或多或少是线性的。

那为什么会这样呢?

我想 JIT 编译器将 int 循环优化到死。并且有充分的理由,因为显然它没有做任何事情。但是为什么long 循环也不这样做呢?

一位同事认为我们可能正在测量 JIT 编译器在 long 循环中的工作,但由于运行时间在 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 插件来真正查看生成的代码:wikis.sun.com/display/HotSpotInternals/PrintAssembly
  • 如果使用 -Xint 标志运行会发生什么?这可以防止 Hotspot 编译,因此您将获得更好的比较。
  • @Steven:但使用-Xint 的结果甚至更少对于任何类似于实际使用的东西都有意义。
  • 我很好奇 PrintAssembly 会显示什么,如果有人想运行它并发布结果...

标签: java performance optimization jit


【解决方案1】:

这是一个有趣的问题,但老实说,我不相信在这里考虑 Hotspot 的行为会产生有用的信息。在一般情况下,您得到的任何答案都不会被转移(因为我们正在研究 Hotspot 在一种特定情况下执行的优化),因此它们将帮助您理解为什么一个无操作比另一个更快, 但它们不会帮助您编写更快的“真实”程序

围绕这类事情编写非常具有误导性的微基准测试也非常容易 - 请参阅 this IBM DW article 了解一些常见的陷阱、如何避免它们以及对您正在做的事情的一些一般性评论。

所以这确实是一个“无评论”的回答,但我认为这是唯一有效的回答。编译时无关紧要的无操作循环需要很快,因此编译器并未优化为在某些情况下快速。

【讨论】:

  • 你当然是对的。我问的原因是纯粹对我在鬼混时注意到的东西感到好奇;我不打算在真正的程序中这样做。 :)
【解决方案2】:

您可能正在使用 32 位 JVM。对于 64 位 JVM,结果可能会有所不同。在 32 位 JVM 中,一个 int 可以映射到本机 32 位整数并通过单个操作递增。同样的情况不会持续很长时间,这将需要更多的操作来增加。

有关 int 和 long 大小的讨论,请参阅此 question

【讨论】:

  • 好点。但是,我使用的是 64 位 JVM。我会更新问题。
  • 发布操作系统和一些有关您的硬件配置(主要是 CPU)的详细信息也会很有用。
【解决方案3】:

我的猜测——这只是一个猜测——是这样的:

JVM 得出结论,第一个循环实际上什么都不做,因此它完全删除了它。没有变量从 for 循环中“逃脱”。

在第二种情况下,循环也不做任何事情。但可能是确定循环不执行任何操作的 JVM 代码具有“if (type of i) == int”子句。在这种情况下,删除无操作 for 循环的优化仅适用于 int。

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

【讨论】:

    【解决方案4】:

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

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

    看看这篇文章,它解释了为什么用 Java 做微基准测试并不像大多数人想象的那么容易:Anatomy of a flawed microbenchmark

    【讨论】:

      猜你喜欢
      • 2017-02-06
      • 1970-01-01
      • 2011-05-08
      • 2020-09-03
      • 2016-12-05
      • 2014-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多