Java 我如何才能确保编译器不会';我不能取消我的性能测试吗?

Java 我如何才能确保编译器不会';我不能取消我的性能测试吗?,java,performance,testing,Java,Performance,Testing,我有一个做一些耗时计算的类。我正在尝试对其进行性能测试: int numValues = 1000000; Random random = new Random(); startMeasuringTime(); double result; for (int i = 0; i < numValues; i++) { result = calculatorInstance.doSomeTimeConsumingCalculationsOn(random.nextDouble());

我有一个做一些耗时计算的类。我正在尝试对其进行性能测试:

int numValues = 1000000;
Random random = new Random();
startMeasuringTime();
double result;
for (int i = 0; i < numValues; i++) {
    result = calculatorInstance.doSomeTimeConsumingCalculationsOn(random.nextDouble());
}
stopMeasuringTime();
int numValues=1000000;
随机=新随机();
开始测量时间();
双重结果;
for(int i=0;i
我使用的是随机值,因此编译器不会将计算优化为一百万倍。但是结果如何呢?编译器是否看到不再使用它并省略了调用(但是,它是否看到方法调用可能产生的任何副作用?)

我不想把结果放在某个地方(放在文件、数组或System.out中),因为我认为这会降低测试的速度,因为我不想测量这些工作。或者产生OutOfMemoryError

提前谢谢


编辑:如果有疑问,请稍微更改标题-查看此测试类的字节码。如果编译器已经“优化”了该调用,那么您将找不到对该方法的调用

是jdk附带的一个小型反汇编程序。将输出转储到文件中,使用标准编辑器打开该文件,然后尝试查找methodname。如果您只想检查某个方法是否在某处被调用或使用,则不必理解整个字节码


进行了快速测试:

public static main(String[] args) {
  for (int i = 0; i < 1000000; i++) {
    double result = doSomething(Math.random());
  }
}

public double doSomething(double random) {
  return random * random;
}
它明确地告诉我们,方法被调用,结果被存储到局部变量中


但是虚拟机仍然可能检测到未使用的局部变量,并且实时编译器在编译实际机器代码时消除了调用。

编译器实际上没有做太多优化(除了计算常量表达式的值),正如已经发现的那样,JVM在优化方面做得更好

现代JVM发现代码可以内联(即,直接插入调用代码而不是执行方法调用),尤其适用于空代码,删除方法调用并替换为-tada-no代码。它运行得很快,但测量不好

除此之外,JVM无法优化调用,因此不必担心传递不同的参数来强制执行求值。如果你的方法是非平凡的,它将被调用

但是,你对微观基准显示错误东西的担忧是正确的。了解性能的更好方法是使用附加的探查器进行实际运行(JDK 6中的jvisualvm中有一个简单的探查器)


您需要知道什么?

确保结果以某种方式使用,例如,求和并在最后打印。 求和是一个很好的选择,因为加法是一种非常便宜的操作

但是结果如何呢?编译器是否看到不再使用它并省略了调用(但是,它是否看到方法调用可能产生的任何副作用?)

视情况而定。如果JIT编译器能够检测到方法调用没有副作用,那么它有权对其进行优化。尤其是因为没有使用结果值。在这种情况下,您可能只是测量对
random.nextDouble()
的调用。。。或者可能是一个空循环

为了确保它不会被优化掉,您可能应该这样写:

int numValues = 1000000;
Random random = new Random();
startMeasuringTime();
double result;
for (int i = 0; i < numValues; i++) {
    result = result +
        calculatorInstance.doSomeCalculationsOn(random.nextDouble());
}
stopMeasuringTime();
System.err.println(result);  // Force result to be computed.
int numValues=1000000;
随机=新随机();
开始测量时间();
双重结果;
for(int i=0;i
(我假设耗时的计算确实取决于参数…)


您还需要考虑JVM预热;i、 e.在JVM中多次运行该基准代码,直到测量的时间稳定



说编译器“过度优化”有点错误。编译器实际上正在正确地执行其工作。如果有什么问题,那就是你的代码有问题;i、 它“没有任何用处”。

优化最有可能由JIT编译器完成。您无法通过字节码确定是否会发生这种情况。你的“快速测试”证明不了什么。关于你的测试,可能会重复。我不知道消耗量是多少,但如果不是很长,你可能会对你的方法和
random.nextDouble()
。我更改了标题。谢谢你的回答!
int numValues = 1000000;
Random random = new Random();
startMeasuringTime();
double result;
for (int i = 0; i < numValues; i++) {
    result = result +
        calculatorInstance.doSomeCalculationsOn(random.nextDouble());
}
stopMeasuringTime();
System.err.println(result);  // Force result to be computed.