:::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. 随机值
@State(Scope.Thread)
public static class SleepyState {
public long sleepTime;
@Setup
public void setup() {
sleepTime = (long) (Math.random() * 1000);
}
}
@Setup默认是Level.Trial类型的
很明显, JVM启动之后Benchmark执行之前, 就已经确定一个随机值 [0,1000) sleepTime, 了
在整个@Benchmark完成之前是不会变的
2. 只在一个JVM中运行Benchmark
代码如下
@Benchmark
@Fork(1)
public void baseline(SleepyState s) throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(s.sleepTime);
}
我们可以知道, 在一个JVM运行, sleepTime不会发生变化, 那么无论执行多少次, baseline的平均执行时间, 都已经确定了
但生产中可能就不是这样, 生产中可能有100个JVM同时支持一个服务, 每个服务启动的时候都生成了自己独有的随机值, 然后再做业务逻辑
如果我们希望从宏观的角度, 观察这种代码在集群中的执行效率, 就需要使用@Fork
3. 增加到5个JVM
@Benchmark
@Fork(5)
public void fork_1(SleepyState s) throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(s.sleepTime);
}
4. 增加到20个JVM
@Benchmark
@Fork(20)
public void fork_2(SleepyState s) throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(s.sleepTime);
}
5. 分析结果
Benchmark Mode Cnt Score Error Units
Sample_13_RunToRun.baseline avgt 264.484 ms/op
Sample_13_RunToRun.fork_1 avgt 5 600.927 ± 1235.737 ms/op
Sample_13_RunToRun.fork_2 avgt 20 583.219 ± 245.996 ms/op
我们可以看到, @Fork 越大, 得到的结果越趋近于期望值
有缘的同学可以自己计算一下期望值, 是(500ms/op)
这样我们又学会JMH的一个用法
当我们的程序需要部署在多个JVM中, 而每个JVM的初始化环境有随机因素
那么我们就可以用@Fork 出多个JVM, 来模拟生产中的环境