如何禁用编译器和JVM优化?

问题描述 投票:19回答:4

我有正在测试Calendar.getInstance().getTimeInMillis()System.currentTimeMilli()的代码:

long before = getTimeInMilli();
for (int i = 0; i < TIMES_TO_ITERATE; i++)
{
  long before1 = getTimeInMilli();
  doSomeReallyHardWork();
  long after1 = getTimeInMilli();
}
long after = getTimeInMilli();
System.out.println(getClass().getSimpleName() + " total is " + (after - before));

我想确保没有JVM或编译器优化发生,因此测试将是有效的,并且实际上将显示出差异。

如何确定?

EDIT:我更改了代码示例,因此将更加清晰。我在这里检查的是在不同的实现中getTimeInMilli()Calendar调用System所花费的时间。

我有这段代码正在测试Calendar.getInstance()。getTimeInMillis()与System.currentTimeMilli():很久以前= getTimeInMilli(); for(int i = 0; i

我认为您需要禁用JIT。将下一个选项添加到您的运行命令中:

-Djava.compiler=NONE

想要

进行优化,因为它将在现实生活中进行-如果JVM的优化方式与您感兴趣的实际情况不同,则该测试无效。

但是,如果要确保JVM不会删除可能会认为无操作的调用,一种选择是使用结果-因此,如果反复调用System.currentTimeMillis(),则可能会求和所有返回值,然后在末尾显示总和。

[请注意,您可能仍然会有一些偏见-例如,如果JVM可以廉价地确定自从上次调用System.currentTimeMillis()以来仅经过了很少的时间,则可以进行一些优化,以便可以使用缓存的值。我并不是说实际上是这种情况,但这是您需要考虑的事情。最终,基准测试只能真正测试您赋予它们的负载。

另外要考虑的事情:假设您要对运行代码

lot

的现实世界进行建模,那么您应该花很多时间运行代码,然后再花点时间-因为Hotspot JVM会逐步优化,并且大概您关心的是经过高度优化的版本,并且do n't要测量JITting和代码的“慢速”版本的时间。正如斯蒂芬所说,您几乎应该确定定时

outside

循环...并且不要忘记实际上use结果...
抱歉,但是您试图做的事没有意义。

如果关闭JIT编译,那么您将只测量关闭JIT编译后调用该方法所花费的时间。这不是有用的信息……因为它几乎不告诉您打开JIT编译时将发生什么事情。

JIT开启和关闭之间的时间可能会有很大的不同。在关闭JIT的情况下,您不太可能想在生产中运行任何东西。

更好的方法是这样做:

long before1 = getTimeInMilli(); for (int i = 0; i < TIMES_TO_ITERATE; i++) { doSomeReallyHardWork(); } long after1 = getTimeInMilli();

...和/或使用纳秒级时钟。


[如果您要测量调用两个版本的getTimeInMillis()所需的时间,那么我不理解您调用doSomeReallyHardWork()的意义。一个更明智的基准是:

public long test() { long before1 = getTimeInMilli(); long sum = 0; for (int i = 0; i < TIMES_TO_ITERATE; i++) { sum += getTimeInMilli(); } long after1 = getTimeInMilli(); System.out.println("Took " + (after - before) + " milliseconds"); return sum; }

...多次调用,直到打印的时间稳定为止。

不管怎样,我的主要观点仍然成立,转向JIT编译和/或优化将意味着您正在测量的东西无助于了解,而不是您真正要找出的东西。 (除非,也就是说,您打算在关闭JIT的情况下在生产环境中运行您的应用程序……我很难相信...)

[您所做的事情看起来像是基准测试,您可以阅读Robust Java benchmarking以获取有关如何正确设置的一些良好背景知识。简而言之,您无需将其关闭,因为这将不会在生产服务器上发生。.相反,您需要知道接近“实时”估计/性能的可能性。在优化之前,您需要“热身”您的代码,它看起来像:

// warm up for (int j = 0; j < 1000; j++) { for (int i = 0; i < TIMES_TO_ITERATE; i++) { long before1 = getTimeInMilli(); doSomeReallyHardWork(); long after1 = getTimeInMilli(); } } // measure time long before = getTimeInMilli(); for (int j = 0; j < 1000; j++) { for (int i = 0; i < TIMES_TO_ITERATE; i++) { long before1 = getTimeInMilli(); doSomeReallyHardWork(); long after1 = getTimeInMilli(); } } long after = getTimeInMilli(); System.out.prinltn( "What to expect? " + (after - before)/1000 ); // average time

[当我们评估代码的性能时,我们使用这种方法,它使我们减少了代码需要工作的实时时间。在分离的方法中更好地测量代码:

public void doIt() { for (int i = 0; i < TIMES_TO_ITERATE; i++) { long before1 = getTimeInMilli(); doSomeReallyHardWork(); long after1 = getTimeInMilli(); } } // warm up for (int j = 0; j < 1000; j++) { doIt() } // measure time long before = getTimeInMilli(); for (int j = 0; j < 1000; j++) { doIt(); } long after = getTimeInMilli(); System.out.prinltn( "What to expect? " + (after - before)/1000 ); // average time

第二种方法更为精确,但也取决于VM。例如。 HotSpot可以执行"on-stack replacement",这意味着如果方法的某些部分执行得非常频繁,它将由VM进行优化,而在执行方法时,旧版本的代码将与经过优化的代码交换。当然,它需要从VM端采取额外的措施。 JRockit不会这样做,仅当再次执行此方法时才会使用代码的优化版本(因此,不执行“运行时”优化...我的意思是在我的第一个代码示例中,将一直执行旧代码...除外) doSomeReallyHardWork内部构件-它们不属于此方法,因此优化会很好地进行。)

更新:正在回答的问题代码已编辑;)

java optimization compiler-construction jvm jit
4个回答
30
投票

我认为您需要禁用JIT。将下一个选项添加到您的运行命令中:


11
投票

想要


3
投票
抱歉,但是您试图做的事没有意义。

3
投票
[您所做的事情看起来像是基准测试,您可以阅读Robust Java benchmarking以获取有关如何正确设置的一些良好背景知识。简而言之,您无需将其关闭,因为这将不会在生产服务器上发生。.相反,您需要知道接近“实时”估计/性能的可能性。在优化之前,您需要“热身”您的代码,它看起来像:
© www.soinside.com 2019 - 2024. All rights reserved.