如果 IIUC 每个分支都会创建一个单独的虚拟机,因为每个虚拟机实例的运行可能会在 JIT 指令中略有不同?
我也很好奇时间属性在下面的注释中做了什么:
@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
TIA,奥莱
JMH提供分叉功能有几个原因。一个是上面 Rafael 讨论的编译配置文件分离。但是这种行为不受@Forks注释的控制(除非你选择 0 分叉,这意味着根本没有子进程分叉来运行基准测试)。可以选择使用预热模式控件 (-wm) 将所有基准测试作为基准预热的一部分运行(从而创建 JIT 要使用的混合配置文件)。
现实情况是,许多事情都可能以某种方式倾斜您的结果,并且多次运行任何基准以建立运行间方差是JMH支持的重要做法(大多数手动滚动框架都无济于事)。运行到运行差异的原因可能包括(但我确信还有更多):
-
CPU 从某个 C 状态开始,在负载面上放大频率,然后过热并缩小。您可以在某些操作系统上控制此问题。
-
进程的内存对齐可能会导致分页行为差异。
- 后台应用程序活动。
- 操作系统的 CPU 分配将有所不同,导致每次运行使用的 CPU 集不同。
- 页面缓存内容和交换
- JIT 编译是并发触发的,可能会导致不同的结果(当测试较大的代码位时,往往会发生这种情况)。请注意,小型单线程基准测试通常不会出现此问题。
- GC 行为的触发时间可能因运行而异,从而导致不同的结果。
使用至少几个分支运行基准测试将有助于消除这些差异,并让您了解在基准测试中看到的运行以运行差异。我建议你从默认值 10 开始,然后根据你的基准实验性地减少(或增加它)。
JVM 通过创建应用程序行为的配置文件来优化应用程序。创建分叉是为了重置此配置文件。否则,运行:
benchmarkFoo();
benchmarkBar();
可能导致的测量值与
benchmarkBar();
benchmarkFoo();
因为第一个基准的轮廓会影响第二个基准。
时间决定了JMH用于预热或运行基准的支出长度。如果这些时间很短,则 VM 可能未充分预热,或者结果的标准偏差可能过高。
更新:
JMH(Java Microbenchmark Harness)已被添加到JDK中 从 JDK 12 开始。
@Fork
注释,指示基准测试的执行方式 value
参数控制基准测试的执行次数,warmup
参数控制基准测试在收集结果之前试运行的次数。
例:
@Benchmark
@Fork(value = 1, warmups = 3)
@BenchmarkMode(Mode.AverageTime)
public void myMethod() {
// Do nothing
}
这指示JMH运行三个预热叉并丢弃结果,然后再进行实时定时基准测试。