Ответ 1
Похоже, проблема связана с работой, выполненной в JDK-7133857, в которой java.lang.Math.pow()
и java.lang.Math.exp()
были наследованы и рассчитаны используя x87.
Эти методы широко используются (!) в профилированном приложении и, следовательно, их значительный эффект.
JDK-8029302 описывает и исправляет проблему для мощности 2 входов; и тестирование приложения с помощью jdk1.8.0_25
(в котором проблема была исправлена) показывает улучшенную производительность, хотя и не возвращается к более высокому уровню jdk1.7.0_25
до того, как была выполнена инициализация.
Вот мои тесты JMH и их результаты для Math.pow()
для трех соответствующих версий JDK:
package org.sample;
import org.openjdk.jmh.annotations.*;
import java.lang.*;
public class MyBenchmark {
@State(Scope.Benchmark)
public static class ThreadState {
volatile double x = 0;
volatile double y = 0;
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public double powx(ThreadState state) {
state.x++;
state.y += 0.5;
return Math.pow(state.x, state.y);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public double pow3(ThreadState state) {
state.x++;
return Math.pow(state.x, 3);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public double pow2(ThreadState state) {
state.x++;
return Math.pow(state.x, 2);
}
}
Результаты:
Intel (R) Core (TM) i5-2520M CPU @2,50 ГГц
jdk1.7.0_25 - до интронизации
# VM invoker: x:\@sdks\jdks\jdk1.7.0_25\jre\bin\java.exe
...
Result: 4877658.355 (99.9%) 330460.323 ops/s [Average]
Statistics: (min, avg, max) = (1216417.493, 4877658.355, 6421780.276), stdev = 1399189.700
Confidence interval (99.9%): [4547198.032, 5208118.678]
# Run complete. Total time: 00:24:48
Benchmark Mode Samples Score Score error Units
o.s.MyBenchmark.pow2 thrpt 200 40160618.138 1561135.596 ops/s
o.s.MyBenchmark.pow3 thrpt 200 3678800.153 88678.269 ops/s
o.s.MyBenchmark.powx thrpt 200 4877658.355 330460.323 ops/s
jdk1.7.0_40 - интригация
# VM invoker: x:\@sdks\jdks\jdk1.7.0_40\jre\bin\java.exe
...
Result: 1860849.245 (99.9%) 94303.387 ops/s [Average]
Statistics: (min, avg, max) = (418909.582, 1860849.245, 2379936.035), stdev = 399286.444
Confidence interval (99.9%): [1766545.859, 1955152.632]
# Run complete. Total time: 00:24:48
Benchmark Mode Samples Score Score error Units
o.s.MyBenchmark.pow2 thrpt 200 9619333.987 230749.333 ops/s
o.s.MyBenchmark.pow3 thrpt 200 9240043.369 238456.949 ops/s
o.s.MyBenchmark.powx thrpt 200 1860849.245 94303.387 ops/s
jdk1.8.0_25 - фиксированная инициализация
# VM invoker: x:\@sdks\jdks\jdk1.8.0_25\jre\bin\java.exe
...
Result: 1898015.057 (99.9%) 92555.236 ops/s [Average]
Statistics: (min, avg, max) = (649562.297, 1898015.057, 2359474.902), stdev = 391884.665
Confidence interval (99.9%): [1805459.821, 1990570.293]
# Run complete. Total time: 00:24:37
Benchmark Mode Samples Score Score error Units
o.s.MyBenchmark.pow2 thrpt 200 81840274.815 1979190.065 ops/s
o.s.MyBenchmark.pow3 thrpt 200 9441518.686 206612.404 ops/s
o.s.MyBenchmark.powx thrpt 200 1898015.057 92555.236 ops/s
Если я читаю это право, сила 2 вопроса, безусловно, была зафиксирована в JDK-8029302 и мощности > 2 ints (I только что протестированная Math.pow(x, 3)
) производительность была улучшена в jdk1.7.0_40
. Что касается странного не-int Math.pow()s
, как сделано выше в тесте powx()
, по-прежнему сохраняется значительная регрессия производительности ( > 3x) при переходе от jdk1.7.0_25
в jdk1.7.0_40
.
Замена Math.pow()
и Math.exp()
их соответствующими методами в org.apache.commons.math3.util.FastMath
полностью решает проблему с увеличением производительности - это правильное решение, насколько мне известно.
Примечание. Это было бы несколько проще, если бы был простой способ (т.е. без требования построения JDK) установить флаг -XX:-InlineIntrinsics
.