How to run methods in benchmarks sequentially with JMH?

断了今生、忘了曾经 提交于 2020-12-13 05:10:11

问题


In my scenario, the methods in benchmark should run sequentially in one thread and modify the state in order.

For example, there is a List<Integer> called num in the benchmark class. What I want is: first, run add() to append a number into the list. Then, run remove() to remove the number from the list.

The calling sequence must be add() --> remove(). If remove() runs before add() or they run concurrently, they would raise exceptions because there's no element in the list.

That is, add() and remove() must be called sequentially and in one thread.

In Control the order of methods using JMH, I learned that the methods run in the lexicographical order. I tried the code below:

@State(Scope.Group)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 10)
public class ListBenchmark {

    private List<Integer> num;

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .verbosity(VerboseMode.NORMAL)
                .syncIterations(true)
                .threads(1)
                .include(".*" + ListBenchmark.class.getCanonicalName() + ".*")
                .build();

        new Runner(options).run();
    }

    @Setup(Level.Invocation)
    public void setup() throws Exception {
        num = new ArrayList<>();
    }

    @Benchmark
    @BenchmarkMode(Mode.SingleShotTime)
    @Group("num")
    public void add() throws Exception {
        num.add(1);
    }

    @Benchmark
    @BenchmarkMode(Mode.SingleShotTime)
    @Group("num")
    public void remove() throws Exception {
        num.remove(0);
    }
}

But it doesn't work, because the add method and the remove method run concurrently. In some cases, the remove runs before add and raises IndexOutOfBoundsException.

How to run the methods in benchmarks sequentially with JMH?


回答1:


You start with a wrong precondition and everything fails short because of that. You can see a broader explanation from the authors here. You want symmetry where asymmetry is implied.

If you want to see how much it takes add -> remove place them both in the same @Benchmark, and same for individual add or remove via different State. For example:

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

    List<Integer> num;

    @Setup(Level.Invocation)
    public void setup() throws Exception {
        num = new ArrayList<>();
    }
}

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

    List<Integer> num;

    @Setup(Level.Invocation)
    public void setup() throws Exception {
        num = new ArrayList<>();
        num.add(1);
    }
}


@Fork(25)
@Benchmark
@BenchmarkMode(Mode.SingleShotTime)
public int add(BothAddAndRemove both) {
    both.num.add(1);
    return both.num.remove(0);
}

@Fork(25)
@Benchmark
@BenchmarkMode(Mode.SingleShotTime)
public int removeOnly(RemoveOnly removeOnly) {
    return removeOnly.num.remove(0);
}


来源:https://stackoverflow.com/questions/65177915/how-to-run-methods-in-benchmarks-sequentially-with-jmh

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!