JMH не работает

JMH показывает одинаковые результаты для разных методов, независимо от того, содержат ли эти методы какой-либо код или нет.

Пример 1: пустой метод для тестирования

public class MyBenchmark {
    public static void main(String[] args) throws Exception {
        org.openjdk.jmh.Main.main(args);
    }

    @Fork(value = 1, warmups = 0)
    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Warmup(iterations = 5)
    public String run() {
        return "done";
    }
}

Результат выполнения этого кода - 1e-8 s/op.

Пример 2: метод с определенной работой:

public class MyBenchmark {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 10000000; i++) {
            list.add(i);
        }
        org.openjdk.jmh.Main.main(args);
    }

    private static List<Integer> list = new ArrayList<>();

    @Fork(value = 1, warmups = 0)
    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Warmup(iterations = 5)
    public String run() {
        List<Integer> copy = new ArrayList<>();
        for (Integer item : list) {
            copy.add(item);
        }
        return "done";
    }
}

Результат тот же: 1e-8 с / оп.

Итак, эталонный тест явно не работает. Что может быть не так?

1 ответ

Решение

Вы используете неправильную шкалу времени - секунды на операции. Кажется, он слишком большой для твоего теста на оп. Просто добавьте следующий параметр @OutputTimeUnit(TimeUnit.NANOSECONDS) к вашему тесту:

import org.openjdk.jmh.annotations.*;

import java.util.concurrent.TimeUnit;

public class MyBenchmark {
    public static void main(String[] args) throws Exception {
        org.openjdk.jmh.Main.main(args);
    }

    @Fork(value = 1, warmups = 0)
    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Warmup(iterations = 5)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public String run() {
        return "done";
    }
}

со следующим результатом:

# Run complete. Total time: 00:00:25

Benchmark        Mode  Cnt  Score   Error  Units
MyBenchmark.run  avgt   20  5.390 ± 0.264  ns/op

Что касается вашего второго примера, он содержит почти все возможные проблемы / подводные камни:

  • устранение мертвого кода - JVM достаточно умен, чтобы обнаружить цикл без побочных эффектов и удалить его из тела метода.
  • неправильное измерение
  • неверная инициализация - JMH имеет специальные аннотации Setup а также State для правильной инициализации бенчмарка

Вот "правильная" версия вашего примера (я только что удалил забывчивые ошибки):

import org.openjdk.jmh.annotations.*;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@State(Scope.Benchmark)
@Fork(value = 1)
public class MyBenchmark {

    private List<Integer> list;

    @Setup
    public void init() {
        list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            list.add(i);
        }
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Warmup(iterations = 5)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public Object run() {
        List<Integer> copy = new ArrayList<>();
        for (Integer item : list) {
            copy.add(item);
        }
        return copy;
    }
}

с задержкой:

# Run progress: 0.00% complete, ETA 00:00:25
# Fork: 1 of 1
# Warmup Iteration   1: 2488116493.000 ns/op
# Warmup Iteration   2: 201271178.600 ns/op

Чтобы понять все возможные проблемы с микробенчмаркингом, пожалуйста, прочитайте следующие примеры.

Другие вопросы по тегам