JMH - 如何测量在 ArrayList 中插入 50M 项所需的时间

JMH - How to measure time it takes to insert 50M items in an ArrayList

我有一个 50M 的 ArrayList,我想测量在其中存储那么多对象所需的时间。似乎所有 JMH 模式都是基于时间的,我们无法真正控制@Benchmark 下代码的执行次数。例如,我如何确保以下代码 运行 每个 fork 恰好 50M 次?

@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(5)
public void run(BenchmarkState state) {
    try {
        state.queue.add(System.nanoTime());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

您可以创建基准 class (ArrayListBenchmark) 和跑步者 class (BenchmarkRunner)。

  • ArrayListBenchmarkclass中,可以添加benchmark方法 迭代所需的次数,将项目添加到 List.
  • BenchmarkRunner class 中,您设置了要添加到的项目的所需数量 List 并配置运行器选项。

注意:根据您的环境,添加 50M 项可能会抛出 OutOfMemoryError.

基准 class:

import java.util.List;
import java.util.ArrayList;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;

public class ArrayListBenchmark {

    @State(Scope.Thread)
    public static class ThreadState {

        @Param({})
        private int items;

        private List<Long> list;

        @Setup(Level.Iteration)
        public void setup() {
            list = new ArrayList<>();
        }
    }

    @Benchmark
    public void addItems(ThreadState state, Blackhole blackhole) {
        blackhole.consume(addItems(state.list, state.items));
    }

    private static boolean addItems(List<Long> list, int items) {
        for (int i = 0; i < items; i++) {
            list.add(System.nanoTime());
        }
        return true;
    }

}

基准跑者class:

import java.util.Locale;
import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.results.format.ResultFormatType;
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;
import org.openjdk.jmh.runner.options.TimeValue;

public class BenchmarkRunner {

    private static final String ITEMS = "items";

    private static final String N_50_000_000 = "50000000";

    public static void main(String[] args) throws RunnerException {
        runArrayListBenchmark();
    }

    public static void runArrayListBenchmark() throws RunnerException {
        Options options = new OptionsBuilder()
                .include(ArrayListBenchmark.class.getSimpleName())
                .mode(Mode.AverageTime)
                .timeUnit(TimeUnit.NANOSECONDS)
                .warmupTime(TimeValue.seconds(1))
                .warmupBatchSize(1)
                .warmupIterations(5)
                .measurementTime(TimeValue.milliseconds(100))
                .measurementBatchSize(1)
                .measurementIterations(10)
                .param(ITEMS, N_50_000_000)
                .operationsPerInvocation(Integer.parseInt(N_50_000_000))
                .threads(1)
                .forks(5)
                .shouldFailOnError(true)
                .shouldDoGC(true)
                .resultFormat(ResultFormatType.CSV)
                .result("target/" + ArrayListBenchmark.class.getSimpleName().toLowerCase(Locale.ENGLISH) + ".csv")
                .build();
        new Runner(options).run();
    }

输出:

Result "ArrayListBenchmark.addItems":
  50.023 ±(99.9%) 0.768 ns/op [Average]
  (min, avg, max) = (48.094, 50.023, 53.020), stdev = 1.551
  CI (99.9%): [49.256, 50.791] (assumes normal distribution)

Benchmark                     (items)  Mode  Cnt   Score   Error  Units
ArrayListBenchmark.addItems  50000000  avgt   50  50.023 ± 0.768  ns/op