Java Language
Repères
Recherche…
Introduction
Ecrire des tests de performances en Java n'est pas aussi simple que d'obtenir System.currentTimeMillis()
au début et à la fin et de calculer la différence. Pour écrire des tests de performances valides, il faut utiliser les outils appropriés.
Exemple JMH simple
JMH est l’un des outils pour écrire des tests de référence appropriés. Disons que nous voulons comparer les performances de recherche d'un élément dans HashSet
et TreeSet
.
Le moyen le plus simple d’intégrer JHM dans votre projet est d’utiliser les plugins maven et shade . Vous pouvez également voir pom.xml
partir d' exemples JHM .
<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>
Après cela, vous devez écrire la classe de référence elle-même:
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));
}
}
N'oubliez pas ce blackhole.consume()
, nous y reviendrons plus tard. Aussi, nous avons besoin de la classe principale pour exécuter le benchmark:
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();
}
}
Et nous sommes tous prêts. Nous avons juste besoin de lancer le mvn package
(il va créer benchmarks.jar
dans votre dossier /target
) et exécuter notre test de mvn package
:
java -cp target/benchmarks.jar benchmark.BenchmarkMain
Et après quelques itérations d'échauffement et de calcul, nous aurons nos résultats:
# 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
A propos de ce blackhole.consume()
. Si vos calculs ne changent pas l’état de votre application, java le ignorera probablement. Donc, pour l'éviter, vous pouvez soit faire en sorte que vos méthodes de référence renvoient une valeur, soit utiliser l'objet Blackhole
pour le consommer.
Vous pouvez trouver plus d'informations sur l'écriture de références dans le blog d'Aleksey Shipilëv , dans le blog de Jacob Jenkov et dans le blog java-performance: 1 , 2 .