Wir arbeiten an einer latenzempfindlichen Anwendung und haben alle Arten von Methoden (mit jmh ) mikrobenchmarkiert . Nachdem ich eine Suchmethode mit einem Mikrobenchmarking versehen und mit den Ergebnissen zufrieden war, implementierte ich die endgültige Version und stellte fest, dass die endgültige Version dreimal langsamer war als das, was ich gerade bewertet hatte.
Der Schuldige war, dass die implementierte Methode ein enum
Objekt anstelle eines zurückgab int
. Hier ist eine vereinfachte Version des Benchmark-Codes:
@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();
}
}
Die Benchmark-Ergebnisse für oben:
# 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
Durch einfaches Ändern des Rückgabetyps der Funktion wurde die Leistung um den Faktor 3 geändert.
Ich dachte, dass der einzige Unterschied zwischen der Rückgabe eines Enum-Objekts und einer Ganzzahl darin besteht, dass einer einen 64-Bit-Wert (Referenz) und der andere einen 32-Bit-Wert zurückgibt. Einer meiner Kollegen vermutete, dass die Rückgabe der Aufzählung zusätzlichen Aufwand verursacht, da die Referenz für potenzielle GC nachverfolgt werden muss. (Angesichts der Tatsache, dass Enum-Objekte statische Endreferenzen sind, erscheint es seltsam, dass dies erforderlich wäre.)
Was ist die Erklärung für den Leistungsunterschied?
AKTUALISIEREN
Ich habe das Maven-Projekt hier geteilt, damit jeder es klonen und den Benchmark ausführen kann. Wenn jemand die Zeit / das Interesse hat, wäre es hilfreich zu sehen, ob andere die gleichen Ergebnisse replizieren können. (Ich habe auf zwei verschiedenen Computern, Windows 64 und Linux 64, repliziert, beide mit Oracle Java 1.7-JVMs). @ ZhekaKozlov sagt, er habe keinen Unterschied zwischen den Methoden gesehen.
So führen Sie Folgendes aus: (nach dem Klonen des Repositorys)
mvn clean install
java -jar .\target\microbenchmarks.jar function.ReturnEnumObjectVersusPrimitiveBenchmark -i 5 -wi 5 -f 1