Как я могу быть уверен, что компилятор не оптимизирует мой тест производительности? - PullRequest
8 голосов
/ 02 февраля 2011

У меня есть класс, который выполняет некоторые трудоемкие вычисления. Я пытаюсь проверить производительность:

int numValues = 1000000;
Random random = new Random();
startMeasuringTime();
double result;
for (int i = 0; i < numValues; i++) {
    result = calculatorInstance.doSomeTimeConsumingCalculationsOn(random.nextDouble());
}
stopMeasuringTime();

Я использую случайные значения, чтобы компилятор не оптимизировал вычисления, чтобы они в миллион раз были одинаковыми. Но как насчет результатов? Видит ли компилятор, что он больше не используется, и пропускает вызов (но может ли он увидеть какие-либо побочные эффекты, которые может иметь вызов метода?)

Я не хочу помещать результаты куда-нибудь (в файл, массив или в System.out), потому что я думаю, что это замедлит тест с работой, которую я не хочу измерять. Или создайте ошибку OutOfMemoryError.

Заранее спасибо.

РЕДАКТИРОВАТЬ: немного изменил заголовок

Ответы [ 4 ]

5 голосов
/ 02 февраля 2011

А как насчет результатов? Видит ли компилятор, что он больше не используется, и пропускает вызов (но может ли он увидеть какие-либо побочные эффекты, которые может иметь вызов метода?)

Это зависит. Если 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.

(Я предполагаю, что длительный расчет зависит от аргумента ...)


Вам также необходимо принять во внимание прогрев JVM; то есть запускать этот эталонный код несколько раз в JVM, пока измеренное время не стабилизируется.


Сказать, что компилятор "чрезмерно оптимизирует", является своего рода ошибкой. Компилятор фактически делает свою работу правильно. Во всяком случае, ошибка в вашем коде; то есть "ничего полезного" не происходит.

3 голосов
/ 02 февраля 2011

Убедитесь, что результат используется каким-либо образом, например, суммируя его и печатая в конце.Суммирование - хороший выбор, потому что сложение - очень дешевая операция.

1 голос
/ 02 февраля 2011

Компилятор фактически не выполняет большую часть оптимизации (за исключением вычисления значений константных выражений), поскольку было обнаружено, что JVM выполняет работу по оптимизации намного лучше.

Именно современные JVM обнаруживают, чтокод может быть встроен (т. е. вставлен непосредственно в вызывающий код вместо выполнения вызова метода), и это особенно хорошо применимо к пустому коду, удаляя вызов метода и заменяя его на - tada - no code.Это работает очень быстро, но не очень хорошо.

Кроме того, JVM не может оптимизировать вызовы, поэтому вам не нужно беспокоиться о передаче других аргументов для обеспечения оценки.Если ваш метод нетривиален, он будет вызван.

Но ваше беспокойство по поводу микропроцессоров, показывающих неправильные вещи, является обоснованным.Гораздо лучший подход к пониманию производительности заключается в том, чтобы выполнить реальный прогон с подключенным профилировщиком (в JDK 6 есть простой jvisualvm).

Что вам нужно знать?

0 голосов
/ 02 февраля 2011

Если есть сомнения - взгляните на байт-код этого тестового класса.Если компилятор «оптимизировал» этот вызов, то вы не найдете там вызова этого метода.

thod - это небольшой дизассемблер, который поставляется с jdk.Сохраните вывод в файл, откройте этот файл в стандартном редакторе и попытайтесь найти имя метода.Вам не нужно понимать весь байт-код, если вы просто хотите проверить, вызывается или используется какой-то определенный метод.


Сделан быстрый тест:

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;
}

Для этого класса байт-код содержит строки

invokestatic   #22; // Method doSomething:(D)D
dstore_2

, которые однозначно сообщают, что метод вызывается и результат сохраняется в локальной переменной.

Но все еще возможно, что виртуальная машина обнаружит неиспользуемую локальную переменную и что компилятор точно в срок устраняет вызов во время компиляции кода реальной машины.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...