Java Language
Ориентиры
Поиск…
Вступление
Написание тестов производительности в Java не так просто, как получение System.currentTimeMillis()
в начале и в конце и вычисление разницы. Чтобы написать допустимые контрольные показатели производительности, следует использовать надлежащие инструменты.
Простой пример JMH
Одним из инструментов для написания надлежащих контрольных тестов является JMH . Предположим, мы хотим сравнить производительность поиска элемента в HashSet
vs TreeSet
.
Самый простой способ получить JHM в ваш проект - это использовать плагин maven и shade . Также вы можете увидеть pom.xml
из примеров 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>
После этого вам нужно написать собственный класс тестов:
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));
}
}
Пожалуйста, имейте в виду этот blackhole.consume()
, мы вернемся к нему позже. Также нам нужен основной класс для тестирования:
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();
}
}
И мы все настроены. Нам просто нужно запустить mvn package
(он будет создавать benchmarks.jar
в вашей /target
папке) и запустить наш тестовый тест:
java -cp target/benchmarks.jar benchmark.BenchmarkMain
И после некоторых повторений разминки и расчета мы получим наши результаты:
# 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
Об этом blackhole.consume()
. Если ваши вычисления не изменят состояние вашего приложения, java, скорее всего, просто проигнорирует его. Таким образом, чтобы избежать этого, вы можете либо заставить свои методы тестирования вернуть некоторую ценность, либо использовать объект Blackhole
для его потребления.
Вы можете найти дополнительную информацию о написании надлежащих тестов в блоге Алексея Шипилева, в блоге Джейкоба Дженкова и в блоге java-performance: 1 , 2 .