Мы работаем над чувствительным к задержкам приложением и проводим микробизнес-анализ всех видов методов (используя jmh). После microbenchmarking метода поиска и удовлетворения результатов, я внедрил окончательную версию, только чтобы найти, что окончательная версия была в 3 раза медленнее, чем то, что я только что тестировал.
Виной было то, что реализованный метод возвращал объект enum
вместо int
. Вот упрощенная версия эталонного кода:
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
public class ReturnEnumObjectVersusPrimitiveBenchmark {
enum Category {
CATEGORY1,
CATEGORY2,
}
@Param( {"3", "2", "1" })
String value;
int param;
@Setup
public void setUp() {
param = Integer.parseInt(value);
}
@Benchmark
public int benchmarkReturnOrdinal() {
if (param < 2) {
return Category.CATEGORY1.ordinal();
}
return Category.CATEGORY2.ordinal();
}
@Benchmark
public Category benchmarkReturnReference() {
if (param < 2) {
return Category.CATEGORY1;
}
return Category.CATEGORY2;
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder().include(ReturnEnumObjectVersusPrimitiveBenchmark.class.getName()).warmupIterations(5)
.measurementIterations(4).forks(1).build();
new Runner(opt).run();
}
}
Результаты тестов выше:
# VM invoker: C:\Program Files\Java\jdk1.7.0_40\jre\bin\java.exe
# VM options: -Dfile.encoding=UTF-8
Benchmark (value) Mode Samples Score Error Units
benchmarkReturnOrdinal 3 thrpt 4 1059.898 ± 71.749 ops/us
benchmarkReturnOrdinal 2 thrpt 4 1051.122 ± 61.238 ops/us
benchmarkReturnOrdinal 1 thrpt 4 1064.067 ± 90.057 ops/us
benchmarkReturnReference 3 thrpt 4 353.197 ± 25.946 ops/us
benchmarkReturnReference 2 thrpt 4 350.902 ± 19.487 ops/us
benchmarkReturnReference 1 thrpt 4 339.578 ± 144.093 ops/us
Простое изменение возвращаемого типа функции изменило производительность почти в 3 раза.
Я думал, что единственная разница между возвратом объекта enum и целым числом состоит в том, что один возвращает 64-битное значение (ссылка), а другое возвращает 32-битное значение. Один из моих коллег предполагал, что возвращение enum добавило дополнительные накладные расходы из-за необходимости отслеживать ссылку для потенциального GC. (Но, учитывая, что объекты перечисления являются статическими окончательными ссылками, кажется странным, что это нужно будет сделать).
В чем объясняется разница в производительности?
ОБНОВЛЕНИЕ
Я поделился проектом maven здесь, чтобы каждый мог клонировать его и запускать тест. Если у кого есть время/интерес, было бы полезно посмотреть, смогут ли другие реплицировать те же результаты. (Я реплицировал на 2 разных компьютерах, Windows 64 и Linux 64, используя оба варианта JVM Oracle Java 1.7). @ZhekaKozlov говорит, что он не видел никакой разницы между этими методами.
Для запуска: (после клонирования репозитория)
mvn clean install
java -jar .\target\microbenchmarks.jar function.ReturnEnumObjectVersusPrimitiveBenchmark -i 5 -wi 5 -f 1