EDIT: maaartinus дал ответ, который я искал, и данные tmyklebu по этой проблеме очень помогли, так что спасибо!:)
Я немного ознакомился с тем, как HotSpot имеет некоторые "встроенные функции", которые вводят в код, особенно для Java standard Math libs (здесь)
Итак, я решил попробовать, чтобы увидеть, насколько сильно может повлиять HotSpot против прямого сравнения (особенно, так как я слышал, что min/max может скомпилироваться в unlessless asm).
public static final int max ( final int a, final int b )
{
if ( a > b )
{
return a;
}
return b;
}
Это моя реализация. Из другого вопроса SO, который я прочитал, с использованием тернарного оператора используется дополнительный регистр, я не обнаружил существенных различий между выполнением блока if и использованием тернарного оператора (т.е. Return (a > b)? A: b).
Выделение массива int 8Mb (т.е. 2 миллиона значений) и рандомизация его, я делаю следующий тест:
try ( final Benchmark bench = new Benchmark( "millis to max" ) )
{
int max = Integer.MIN_VALUE;
for ( int i = 0; i < array.length; ++i )
{
max = OpsMath.max( max, array[i] );
// max = Math.max( max, array[i] );
}
}
Я использую объект Benchmark в блоке try-with-resources. Когда он заканчивается, он вызывает функцию close() на объекте и печатает время завершения блока. Тесты выполняются отдельно, комментируя ввод/вывод максимальных вызовов в приведенном выше коде.
'max' добавляется в список вне контрольного блока и печатается позже, поэтому, чтобы избежать JVM, оптимизирующего весь блок.
Массив рандомизируется каждый раз, когда выполняется тест.
Запуск теста 6 раз, он дает следующие результаты:
Java standard Math:
millis to max 9.242167
millis to max 2.1566199999999998
millis to max 2.046396
millis to max 2.048616
millis to max 2.035761
millis to max 2.001044
Достаточно стабильный после первого запуска, а запуск тестов снова дает аналогичные результаты.
OpsMath:
millis to max 8.65418
millis to max 1.161559
millis to max 0.955851
millis to max 0.946642
millis to max 0.994543
millis to max 0.9469069999999999
Опять же, очень стабильные результаты после первого запуска.
Вопрос: Почему?. Там есть большая разница. И я понятия не имею, почему. Даже если я реализую свой метод max() точно, например Math.max() (т.е. Return (a >= b)? A: b) Я все равно получаю лучшие результаты! Это не имеет никакого смысла.
Технические характеристики:
Процессор: Intel i5 2500, 3,3 ГГц. Версия Java: JDK 8 (публикация 18 марта), x64. Debian Jessie (тестовый выпуск) x64.
Мне еще нужно попробовать с 32-битным JVM.
EDIT: Самостоятельный тест по запросу. Добавлена строка, чтобы заставить JVM предварительно загружать классы Math и OpsMath. Это исключает стоимость 18 мс первой итерации для теста OpsMath.
// Constant nano to millis.
final double TO_MILLIS = 1.0d / 1000000.0d;
// 8Mb alloc.
final int[] array = new int[(8*1024*1024)/4];
// Result and time array.
final ArrayList<Integer> results = new ArrayList<>();
final ArrayList<Double> times = new ArrayList<>();
// Number of tests.
final int itcount = 6;
// Call both Math and OpsMath method so JVM initializes the classes.
System.out.println("initialize classes " +
OpsMath.max( Math.max( 20.0f, array.length ), array.length / 2.0f ));
final Random r = new Random();
for ( int it = 0; it < itcount; ++it )
{
int max = Integer.MIN_VALUE;
// Randomize the array.
for ( int i = 0; i < array.length; ++i )
{
array[i] = r.nextInt();
}
final long start = System.nanoTime();
for ( int i = 0; i < array.length; ++i )
{
max = Math.max( array[i], max );
// OpsMath.max() method implemented as described.
// max = OpsMath.max( array[i], max );
}
// Calc time.
final double end = (System.nanoTime() - start);
// Store results.
times.add( Double.valueOf( end ) );
results.add( Integer.valueOf( max ) );
}
// Print everything.
for ( int i = 0; i < itcount; ++i )
{
System.out.println( "IT" + i + " result: " + results.get( i ) );
System.out.println( "IT" + i + " millis: " + times.get( i ) * TO_MILLIS );
}
Результат Java Math.max:
IT0 result: 2147477409
IT0 millis: 9.636998
IT1 result: 2147483098
IT1 millis: 1.901314
IT2 result: 2147482877
IT2 millis: 2.095551
IT3 result: 2147483286
IT3 millis: 1.9232859999999998
IT4 result: 2147482828
IT4 millis: 1.9455179999999999
IT5 result: 2147482475
IT5 millis: 1.882047
Результат OpsMath.max:
IT0 result: 2147482689
IT0 millis: 9.003616
IT1 result: 2147483480
IT1 millis: 0.882421
IT2 result: 2147483186
IT2 millis: 1.079143
IT3 result: 2147478560
IT3 millis: 0.8861169999999999
IT4 result: 2147477851
IT4 millis: 0.916383
IT5 result: 2147481983
IT5 millis: 0.873984
Все те же общие результаты. Я попытался с рандомизацией массива только один раз и повторил тесты по одному и тому же массиву, я получаю более быстрые результаты в целом, но та же 2x разница между Java Math.max и OpsMath.max.