我认为您需要禁用JIT。将下一个选项添加到您的运行命令中:
我有正在测试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不会删除可能会认为无操作的调用,一种选择是使用结果-因此,如果反复调用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的情况下在生产环境中运行您的应用程序……我很难相信...)
// 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
内部构件-它们不属于此方法,因此优化会很好地进行。)更新:正在回答的问题代码已编辑;)
我认为您需要禁用JIT。将下一个选项添加到您的运行命令中:
您想要