当前位置: 首页 > 知识库问答 >
问题:

在JMH中恢复每个基准测试后的Java列表状态

高博涉
2023-03-14

我想制作一个基准来测量插入List的时间,比较ArrayListLinkedList并使用不同的初始大小。所以我使用JMH为这些目标创建了一个基准:

@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@Fork(value = 3, jvmArgsAppend = {"-XX:+UseParallelGC", "-Xms1g", "-Xmx1g"})
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class ListBench {

    @Param({"100", "1000", "10000", "100000"})
    int size;

    @Param({"arraylist", "linkedlist"})
    String type;

    private List<Item> target;

    private Item insert;

    @Setup
    public void setup() {
        this.target = switch (this.type) {
            case "arraylist" -> new ArrayList<>();
            case "linkedlist" -> new LinkedList<>();
            default -> {
                throw new IllegalStateException(String.format("target is `%s`", this.target));
            }
        };
        IntStream.range(0, this.size).mapToObj(Item::new).forEach(this.target::add);
        this.insert = new Item(-1);
    }

    @Benchmark
    public void insertFirst() {
        this.target.add(0, this.insert);
    }

    @Benchmark
    public void insertMiddle() {
        this.target.add(this.size / 2, this.insert);
    }

    @Benchmark
    public void insertLast() {
        this.target.add(this.insert);
    }

    static class Item {

        private final int val;

        Item(final int val) {
            this.val = val;
        }

        // + equals and hashCode
    }
}

这个基准测试的问题是,它在迭代之间共享一个状态,因此初始大小100会多次调用insertFirst,并增加目标的大小。每次调用之前,我都会查看@Setup(Level.Invocation)来初始化target,但是javadocs中的警告说它不适合我的情况:

此级别仅适用于每次{@link Benchmark}方法调用超过一毫秒的基准测试。在临时的基础上验证对您的案例的影响也是一个好主意。

每次插入都不到一毫秒,比设置更快。

我能用JMH做些什么来只测量add调用不同的目标和大小,每次调用使用相同大小的目标?

共有1个答案

戚阳曜
2023-03-14

您应该将您的状态作为参数传递给每个基准测试,同样为了避免死代码消除,您应该传递一个黑洞或在每个基准测试方法中返回值。为了更好地理解,请参阅以下代码片段:

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

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

public class MyBenchmark {

    @State(Scope.Benchmark)
    public static class ArrayListState {

        @Param({"100", "1000", "10000", "100000"})
        int size;

        private List<Item> target = new ArrayList<>();
    }

    @State(Scope.Benchmark)
    public static class LinkedListState {

        @Param({"100", "1000", "10000", "100000"})
        int size;

        private List<Item> target = new LinkedList<>();
    }

    @Benchmark
    public void arrayListBenchmark(ArrayListState state, Blackhole blackhole) {
        for (int i = 0; i < state.size; i++) {
            state.target.add(new Item(-1));
        }
        // rest of the code to be measured
        blackhole.consume(state);
    }

    @Benchmark
    public void linkedListBenchmark(LinkedListState state, Blackhole blackhole) {
        for (int i = 0; i < state.size; i++) {
            state.target.add(new Item(-1));
        }
        // rest of the code to be measured
        
        blackhole.consume(state);
    }


    static class Item {

        private final int val;

        Item(final int val) {
            this.val = val;
        }

        // + equals and hashCode
    }

}
 类似资料:
  • 我正在使用向服务器发送REST请求的自定义Java库为HTTP服务器编写性能测试。在开始时,我正在执行数据准备阶段,以便获得一个要向服务器发送请求的对象列表。 现在,问题是,我可以使用注释测试可以注入基准函数的参数列表: 问题是,我希望通过Java参数列表实现同样的效果,并避免对它们进行迭代。你能告诉我怎么做吗?

  • 我的最终目标是使用标准Java集合作为基准,为几个Java基本集合库创建一套全面的基准。在过去,我曾使用循环方法编写这类微基准测试。我将我正在进行基准测试的函数放在一个循环中,并迭代100万次,这样jit就有机会预热。我取循环的总时间,然后除以迭代次数,得到一个对我正在进行基准测试的函数的单个调用所需时间的估计值。在最近阅读了JMH项目,特别是这个例子:JMHSample_11_循环之后,我看到了

  • 我使用JMH对DOM解析器进行基准测试。我得到了非常奇怪的结果,因为第一次迭代实际上比后面的迭代运行得更快 有人能解释为什么会这样吗?此外,百分位数和所有数字意味着什么?为什么在第三次迭代后它开始变得稳定?一次迭代是否意味着整个基准测试方法的一次迭代?下面是我正在运行的方法

  • 我使用 jmh(http://openjdk.java.net/projects/code 工具/jmh/ ) 来基准测试一些方法。此外,我有一组参数,我想用它们作为参数来运行此方法。是否可以为每个特定参数值生成一个方法(带有@GenerateMicroBenchmark注释)? 现在我使用类似的实现,但不太方便,因为我必须手动编写很多统一的代码:

  • 受另一个关于堆栈溢出的问题的启发,我编写了一个微型基准来检查,什么更有效: 有条件地检查零除数或 捕获和处理 下面是我的代码: 我对JMH完全陌生,不确定代码是否正确。 我的基准是正确的吗?你看到任何错误吗? 旁白:请不要建议询问https://codereview.stackexchange.com.对于Codereview,代码必须已按预期工作。我不确定这个基准是否能按预期工作。

  • 考虑下面的jmh测试。 为函数和的广泛参数进行基准测试的最佳方法是什么?@Param仅适用于少量不同的输入,可能会影响结果的准确性。其他方法(如生成输入数组和增加基准内部的索引)会导致基准方法的状态更改。 从基准测试方法更改状态通常是一个坏主意吗?