Szukaj…


Wprowadzenie

Pisanie testów wydajności w Javie nie jest tak proste, jak pobranie System.currentTimeMillis() na początku i na końcu i obliczenie różnicy. Aby pisać prawidłowe testy wydajności, należy używać odpowiednich narzędzi.

Prosty przykład JMH

Jednym z narzędzi do pisania odpowiednich testów porównawczych jest JMH . Powiedzmy, że chcemy porównać wydajność wyszukiwania elementu w HashSet i TreeSet .

Najprostszym sposobem na włączenie JHM do swojego projektu - jest użycie wtyczki maven i shadow . Możesz także zobaczyć pom.xml z przykładów 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>

Następnie musisz napisać samą klasę porównawczą:

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));
    }
}

Pamiętaj o tym blackhole.consume() , do niego wrócimy później. Potrzebujemy także głównej klasy do przeprowadzania testu porównawczego:

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();
    }
}

I wszystko gotowe. Musimy tylko uruchomić mvn package (spowoduje to utworzenie benchmarks.jar w folderze /target ) i uruchomienie naszego testu porównawczego:

java -cp target/benchmarks.jar benchmark.BenchmarkMain

Po kilku rozgrzewkach i iteracjach obliczeniowych uzyskamy nasze wyniki:

# 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

O tym blackhole.consume() . Jeśli twoje obliczenia nie zmienią stanu twojej aplikacji, Java najprawdopodobniej po prostu ją zignoruje. Aby tego uniknąć, możesz sprawić, by metody Blackhole pewną wartość lub użyć obiektu Blackhole aby ją wykorzystać.

Więcej informacji na temat pisania odpowiednich testów porównawczych można znaleźć na blogu Alekseya Shipilëva, blogu Jacoba Jenkova i blogu poświęconym wydajności: 1 , 2 .



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow