Java Language
benchmarks
Sök…
Introduktion
Att skriva riktmärken i java är inte så enkelt som att få System.currentTimeMillis()
i början och i slutet och beräkna skillnaden. För att skriva giltiga prestanda benchmarks, bör man använda lämpliga verktyg.
Enkelt JMH-exempel
Ett av verktygen för att skriva riktiga benchmarktester är JMH . Låt oss säga att vi vill jämföra prestanda för att söka efter ett element i HashSet
vs TreeSet
.
Det enklaste sättet att få JHM till ditt projekt - är att använda maven och skugga plugin. Du kan också se pom.xml
från JHM-exempel .
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>/benchmarks</finalName>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.18</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.18</version>
</dependency>
</dependencies>
Efter detta måste du skriva riktmärket själv:
package benchmark;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
@State(Scope.Thread)
public class CollectionFinderBenchmarkTest {
private static final int SET_SIZE = 10000;
private Set<String> hashSet;
private Set<String> treeSet;
private String stringToFind = "8888";
@Setup
public void setupCollections() {
hashSet = new HashSet<>(SET_SIZE);
treeSet = new TreeSet<>();
for (int i = 0; i < SET_SIZE; i++) {
final String value = String.valueOf(i);
hashSet.add(value);
treeSet.add(value);
}
stringToFind = String.valueOf(new Random().nextInt(SET_SIZE));
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void testHashSet(Blackhole blackhole) {
blackhole.consume(hashSet.contains(stringToFind));
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void testTreeSet(Blackhole blackhole) {
blackhole.consume(treeSet.contains(stringToFind));
}
}
Kom ihåg detta blackhole.consume()
, vi kommer tillbaka till det senare. Vi behöver också huvudklass för att köra riktmärke:
package benchmark;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
public class BenchmarkMain {
public static void main(String[] args) throws RunnerException {
final Options options = new OptionsBuilder()
.include(CollectionFinderBenchmarkTest.class.getSimpleName())
.forks(1)
.build();
new Runner(options).run();
}
}
Och vi är redo. Vi behöver bara köra mvn package
(det skapar benchmarks.jar
i din /target
målmapp) och kör vårt benchmark-test:
java -cp target/benchmarks.jar benchmark.BenchmarkMain
Och efter några uppvärmnings- och beräkningsuppdateringar får vi våra resultat:
# Run complete. Total time: 00:01:21
Benchmark Mode Cnt Score Error Units
CollectionFinderBenchmarkTest.testHashSet avgt 20 9.940 ± 0.270 ns/op
CollectionFinderBenchmarkTest.testTreeSet avgt 20 98.858 ± 13.743 ns/op
Om den blackhole.consume()
. Om dina beräkningar inte ändrar din ansökningstillstånd kommer java troligen att ignorera det. Så för att undvika det kan du antingen få dina benchmarkmetoder att ge tillbaka något värde eller använda Blackhole
objekt för att konsumera det.
Du kan hitta mer information om hur du skriver riktiga riktmärken i Aleksey Shipilëvs blogg , i Jacob Jenkovs blogg och i java-performance-blogg: 1 , 2 .