:::warning https://github.com/openjdk/jmh/blob/master/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_13_RunToRun.java :::

很多时候, 我们有些随机值是在JVM启动的时候就确定了, 比如说有些随机数的种子之类的东西
那么如果我们benchmark一直在一个JVM执行1万万万万次, 得到的结果也离期望值很远
所以我们要用@Fork 注解, 让我们的Benchmark再尽量多的JVM下执行

示例

这里我们讨论分析一下官方的13个示例, 为什么要用@Fork

1. 随机值

  1. @State(Scope.Thread)
  2. public static class SleepyState {
  3. public long sleepTime;
  4. @Setup
  5. public void setup() {
  6. sleepTime = (long) (Math.random() * 1000);
  7. }
  8. }

@Setup默认是Level.Trial类型的
很明显, JVM启动之后Benchmark执行之前, 就已经确定一个随机值 [0,1000) sleepTime, 了
在整个@Benchmark完成之前是不会变的

2. 只在一个JVM中运行Benchmark

代码如下

  1. @Benchmark
  2. @Fork(1)
  3. public void baseline(SleepyState s) throws InterruptedException {
  4. TimeUnit.MILLISECONDS.sleep(s.sleepTime);
  5. }

我们可以知道, 在一个JVM运行, sleepTime不会发生变化, 那么无论执行多少次, baseline的平均执行时间, 都已经确定了

但生产中可能就不是这样, 生产中可能有100个JVM同时支持一个服务, 每个服务启动的时候都生成了自己独有的随机值, 然后再做业务逻辑

如果我们希望从宏观的角度, 观察这种代码在集群中的执行效率, 就需要使用@Fork

3. 增加到5个JVM

  1. @Benchmark
  2. @Fork(5)
  3. public void fork_1(SleepyState s) throws InterruptedException {
  4. TimeUnit.MILLISECONDS.sleep(s.sleepTime);
  5. }

4. 增加到20个JVM

  1. @Benchmark
  2. @Fork(20)
  3. public void fork_2(SleepyState s) throws InterruptedException {
  4. TimeUnit.MILLISECONDS.sleep(s.sleepTime);
  5. }

5. 分析结果

  1. Benchmark Mode Cnt Score Error Units
  2. Sample_13_RunToRun.baseline avgt 264.484 ms/op
  3. Sample_13_RunToRun.fork_1 avgt 5 600.927 ± 1235.737 ms/op
  4. Sample_13_RunToRun.fork_2 avgt 20 583.219 ± 245.996 ms/op


我们可以看到, @Fork 越大, 得到的结果越趋近于期望值
有缘的同学可以自己计算一下期望值, 是(500ms/op)

这样我们又学会JMH的一个用法
当我们的程序需要部署在多个JVM中, 而每个JVM的初始化环境有随机因素
那么我们就可以用@Fork 出多个JVM, 来模拟生产中的环境