JMH:在所有Benchmark测试中使用相同的静态对象

时间:2015-11-18 21:31:22

标签: java jmh

我有一个构造一些复杂数据的类(想象一下大型XML或JSON结构 - 那种事情)。构建它需要时间。所以我想构建一次,然后在所有测试中使用相同的数据。目前我基本上在一个定义public static的类中定义了一个main对象实例,然后在测试中明确地引用它(代码是一个非常简单的例子):

public class Data 
{
    // This class constructs some complicated data 
}

public class TestSet 
{
    public static final Data PARSE_ME = new Data(...);

    public static void main(String[] args) throws RunnerException 
    {
        Options opt = new OptionsBuilder()
                .include(".*ParserTest") // several tests
                .forks(1)
                .build();

        new Runner(opt).run();
    }
}

@State(Scope.Thread)
public class SomeParserTest
{
    @Setup(Level.Iteration)
    public void setup()
    {
        Parser parser = new Parser(TestSet.PARSE_ME);
    }

    @Benchmark
    public void getId() 
    {
        parser.getId(123);
    }
}

这当然很可怕......一个同样邪恶的选择就是创建一个单独的类,以便它可以容纳一个静态对象。使用像

这样的东西会很好
Options opt = new OptionsBuilder()
    ...
    .param(/*my Data object comes here*/)

但是param只接受字符串,所以不确定如何传递一个对象(更重要的是:对象的相同实例!)。

上面描述的那样,还有一个比全局物体更优雅的东西吗?

2 个答案:

答案 0 :(得分:11)

不幸的是,JMH无法在基准测试之间共享数据。

首先,当一个基准测试可以默默地修改另一个基准测试的输入数据时,这会破坏基准测试隔离,从而使比较不正确。这就是为什么每个基准测试都需要@Setup @State个对象。

但更重要的是,无论你在基准测试之间共享数据(例如,从两者都可访问的static字段),你构建的任何技巧都会在默认的“分叉”模式下中断,此时JMH将在自己的VM中执行每个测试。值得注意的是,您建议使用static final Data TestSet.PARSE_ME的内容实际上会为每个@Benchmark执行,因为无论如何,每个新的VM实例都必须初始化TestSet;)但是,您可以禁用分叉,但是引入的问题多于解决的问题。

因此,最好花些时间让设置成本更容易忍受,这样就不会让人痛苦不堪。例如,从磁盘反序列化数据而不是计算它。或者,想出一种更快的计算方法。

答案 1 :(得分:0)

我只是将所有基准测试方法移动到一个类中,将状态对象定义为内部类,并将状态注入每个类:

public class ParserBenchmarks {

  @State(Scope.Thread)
  public static class StateHolder {
    Parser parser = null;

    @Setup(Level.Iteration)
    public void setup()
    {
      parser = new Parser(TestSet.PARSE_ME);
    }

    public Parser getParser() {
      return parser;
    }
  }

  @Benchmark
  public int getId_123(StateHolder stateHolder) {
    return stateHolder.getParser().getId(123);
  }

  @Benchmark
  public int getId_456(StateHolder stateHolder) {
    return stateHolder.getParser().getId(456);
  }

}

请注意,所有基准测试方法都应返回值,否则编译器可能会将其作为死代码删除。