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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5242405/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 10:09:53  来源:igfitidea点击:

How to disable compiler and JVM optimizations?

javaoptimizationcompiler-constructionjvmjit

提问by oshai

I have this code that is testing Calendar.getInstance().getTimeInMillis()vs System.currentTimeMilli():

我有这个测试Calendar.getInstance().getTimeInMillis()vs 的代码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));

I want to make sure no JVM or compiler optimization happens, so the test will be valid and will actually show the difference.

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

How to be sure?

怎么确定?

EDIT: I changed the code example so it will be more clear. What I am checking here is how much time it takes to call getTimeInMilli()in different implementations - Calendarvs System.

编辑:我更改了代码示例,因此会更清楚。我在这里检查的是getTimeInMilli()在不同的实现中调用需要多少时间-CalendarSystem.

回答by ilalex

I think you need to disable JIT. Add to your run command next option:

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

-Djava.compiler=NONE

回答by Jon Skeet

You wantoptimization to happen, because it will in real life - the test wouldn't be valid if the JVM didn't optimize in the same way that it would in the real situation you're interested in.

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

However, if you want to make sure that the JVM doesn't remove calls that it could potentially consider no-ops otherwise, one option is to use the result - so if you're calling System.currentTimeMillis()repeatedly, you might sum all the return values and then display the sum at the end.

但是,如果您想确保 JVM 不会删除它可能会考虑无操作的调用,则一种选择是使用结果 - 因此,如果您System.currentTimeMillis()重复调用,您可能会将所有返回值和然后在最后显示总和。

Note that you may still have some bias though - for example, there may be some optimization if the JVM can cheaply determine that only a tiny amount of time has passed since the last call to System.currentTimeMillis(), so it can use a cached value. I'm not saying that's actuallythe case here, but it's the kind of thing you need to think about. Ultimately, benchmarks can only really test the loads you give them.

请注意,您可能仍然有一些偏见 - 例如,如果 JVM 可以廉价地确定自上次调用以来只过去了一小段时间,那么可能会有一些优化System.currentTimeMillis(),因此它可以使用缓存值。我并不是说这里的情况确实如此,但这是您需要考虑的事情。最终,基准测试只能真正测试您提供给它们的负载。

One other thing to consider: assuming you want to model a real world situation where the code is run a lot, you should run the code a lot before taking any timing - because the Hotspot JVM will optimize progressively harder, and presumably you care about the heavily-optimized version and don'twant to measure the time for JITting and the "slow" versions of the code.

另一件需要考虑的事情:假设你想模拟一个真实世界的情况,其中代码运行很多,你应该在进行任何计时之前运行代码 - 因为 Hotspot JVM 将逐步优化,并且大概你关心重优化的版本,并没有要衡量JITting和代码的“慢”版本的时间。

As Stephen mentioned, you should almost certainly take the timing outsidethe loop... and don't forget to actually usethe results...

正如斯蒂芬提到的,您几乎肯定应该将时间安排在循环之外……并且不要忘记实际使用结果……

回答by Maxym

What you are doing looks like benchmarking, you can read Robust Java benchmarkingto get some good background about how to make it right. In few words, you don't need to turn it off, because it won't be what happens on production server.. instead you need to know the close the possible to 'real' time estimation / performance. Before optimization you need to 'warm up' your code, it looks like:

您正在做的事情看起来像基准测试,您可以阅读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

When we measure performance of our code we use this approach, it give us more less real time our code needs to work. Even better to measure code in separated methods:

当我们测量代码的性能时,我们使用这种方法,它使我们的代码需要工作的实时性更少。更好地以分离的方法测量代码:

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

Second approach is more precise, but it also depends on VM. E.g. HotSpot can perform "on-stack replacement", it means that if some part of method is executed very often it will be optimized by VM and old version of code will be exchanged with optimized one while method is executing. Of course it takes extra actions from VM side. JRockit does not do it, optimized version of code will be used only when this method is executed again (so no 'runtime' optimization... I mean in my first code sample all the time old code will be executed... except for doSomeReallyHardWorkinternals - they do not belong to this method, so optimization will work well).

第二种方法更精确,但也取决于 VM。例如,HotSpot 可以执行“栈上替换”,这意味着如果方法的某些部分被频繁执行,VM 将对其进行优化,并且在方法执行时将旧版本的代码与优化的代码进行交换。当然,它需要从 VM 端采取额外的行动。JRockit 不这样做,只有在再次执行此方法时才会使用优化版本的代码(因此没有“运行时”优化……我的意思是在我的第一个代码示例中,所有时间都将执行旧代码……除了doSomeReallyHardWork内部 - 它们不属于这种方法,所以优化会很好地工作)。

UPDATED: code in question was edited while I was answering ;)

更新:有问题的代码在我回答时被编辑;)

回答by Stephen C

Sorry, but what you are trying to do makes little sense.

抱歉,但您尝试做的事情毫无意义。

If you turn off JIT compilation, then you are only going to measure how long it takes to call that method with JIT compilation turned off. This is not useful information ... because it tells you little if anything about what will happen when JIT compilation is turned on.

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

The times between JIT on and off can be different by a huge factor. You are unlikely to want to run anything in production with JIT turned off.

JIT 开启和关闭之间的时间可能相差很大。您不太可能希望在关闭 JIT 的情况下在生产中运行任何东西。

A better approach would be to do this:

更好的方法是这样做:

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

... and / or use the nanosecond clock.

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



If you are trying to measure the time taken to call the two versions of getTimeInMillis(), then I don't understand the point of your call to doSomeReallyHardWork(). A more senible benchmark would be this:

如果您试图测量调用 的两个版本所花费的时间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;
}

... and call that a number of times, until the times printed stabilize.

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

Either way, my main point still stands, turning of JIT compilation and / or optimization would mean that you were measuring something that is not useful to know, and not what you are really trying to find out. (Unless, that is, you are intending to run your application in production with JIT turned off ... which I find hard to believe ...)

无论哪种方式,我的主要观点仍然存在,转向 JIT 编译和/或优化将意味着您正在测量一些不需要知道的东西,而不是您真正想要找出的东西。(除非,也就是说,您打算在关闭 JIT 的情况下在生产环境中运行您的应用程序……我觉得难以置信……)