我有一个构造一些复杂数据的类(想象一下大型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
只接受字符串,所以不确定如何传递一个对象(更重要的是:对象的相同实例!)。
上面描述的那样,还有一个比全局物体更优雅的东西吗?
答案 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);
}
}
请注意,所有基准测试方法都应返回值,否则编译器可能会将其作为死代码删除。