2011-02-02 2 views
7

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

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.

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

EDIT: изменил название немного

+1

Возможный дубликат [Как написать правильный микро-тест в Java?] (Http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in -java) –

+1

Что касается вашего теста, я не знаю, как потреблять «doSomeTimeConsumingCalculationsOn», но если он не так длинный *, вы, вероятно, сравниваете как ваш метод, так и 'random.nextDouble()'. – gabuzo

ответ

5

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

От этого зависит. Если компилятор 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 до тех пор, пока измеренное время не стабилизируется.


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

+0

Я изменил название. Спасибо за ваш ответ! – nokul

0

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

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 

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

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

+3

Оптимизация, скорее всего, будет выполнена компилятором JIT. Вы не можете определить, будет ли это происходить от байт-кодов. Ваш «быстрый тест» ничего не доказывает. –

1

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

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

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

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

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

3

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

+0

Подведение итогов - это очень хорошая идея! +1 – nokul

Смежные вопросы