乐趣区

如果你想写自己的Benchmark框架

简介

应用过 JMH 的同学肯定会惊叹它的神奇。JMH 作为一个优良的 Benchmark 框架带给了咱们有数的欢畅。作为一个有极客精力的程序员,那么有没有想过来本人实现一个 Benchmark 框架呢?

在实现 Benchmark 框架的时候有须要留神些什么问题呢?快来一起看看吧。

八条军规

这里叫军规实际上不适合,只是借用一下军规的来彰显一下声势!大家不要太介意。

第一条军规

工欲善其事,必先利其器。想写好一个 JMH 当然须要深刻理解 JVM 的运行原理,包含 JIT,C1,C2 编译器和他们的分层编译原理,JIT 运行时的编译优化,包含 Loop unrolling, Inlining, Dead Code Elimination,
Escape analysis, Intrinsics, Branch prediction 等等。

当然,最好是参考一下大牛们写过的 JMH 框架,找点灵感。

最初大家要理解,Benchmark 框架不是万能的。它只是在特定的环境中 JVM 的体现。

因为在 Benchmark 中咱们必定是要做循环的,一般来说就是某某办法运行多少次,这种比较简单的循环。实际上,JVM 运行的代码是非常复杂的。Benchmark 远远不能代表 JVM 的全副。

然而,见微知著,应用 Benchmark 还是能够一窥 JVM 的机密的。

第二条军规

在 JMH 中,咱们个别须要设置 warmup 和 measurement 的次数:

@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)

这是为什么呢?咱们晓得 JIT 中的代码是动静编译成为机器码的,并且是须要肯定的工夫的。

只有 JIT 检测到你这是热点代码,才会对其进行优化。

咱们检测代码的性能,个别是指代码在稳固运行的环境中的情景。而不是指第一次或者前几次运行的时候,因为这个时候,这些代码可能并没有被编译成机器码。这样的进去的后果往往是和理论不相符的。

第三条军规

在编写 Benchmark 的同时,肯定要开启 JVM 的日志。例如:-XX:+PrintCompilation, -verbose:gc 等。

为什么呢?

大家想想 benchmark 是做什么的呢?就是统计工夫的。

咱们心愿在运行 benchmark 的时候,JVM 不要做任何不属于运行代码的任何事件,否则就可能会影响到 benchmark 的准确性。

所以开启 JVM 的日志就是为了做校验。不要在做 benchmark 的时候有其余操作。

第四条军规

留神 JIT 的分层编译。

因为 Client VM 和 Server VM 的呈现,所以在 JIT 中呈现了两种不同的编译器,C1 for Client VM,C2 for Server VM。

因为 javac 的编译只能做大量的优化,其实大量的动静优化是在 JIT 中做的。C2 绝对于 C1,其优化的水平更深,更加激进。

为了更好的晋升编译效率,JVM 在 JDK7 中引入了分层编译 Tiered compilation 的概念。

对于 JIT 自身来说,动静编译是须要占用用户内存空间的,有可能会造成较高的提早。

对于 Server 服务器来说,因为代码要服务很多个 client,所以磨刀不误砍柴工,短暂的提早带来永恒的收益,听起来是能够承受的。

Server 端的 JIT 编译也不是立马进行的,它可能须要收集到足够多的信息之后,才进行编译。

而对于 Client 来说,提早带来的性能影响就须要进行思考了。和 Server 相比,它只进行了简略的机器码的编译。

为了满足不同档次的编译需要,于是引入了分层编译的概念。

大略来说分层编译能够分为三层:

  1. 第一层就是禁用 C1 和 C2 编译器,这个时候没有 JIT 进行。
  2. 第二层就是只开启 C1 编译器,因为 C1 编译器只会进行一些简略的 JIT 优化,所以这个能够应答惯例状况。
  3. 第三层就是同时开启 C1 和 C2 编译器。

在 JDK7 中,你能够应用上面的命令来开启分层编译:

-XX:+TieredCompilation

而在 JDK8 之后,祝贺你,分层编译曾经是默认的选项了,不必再手动开启。

Client 编译和 Server 编译,甚至是 OSR 都是不同的。大家在写 Benchmark 的时候肯定要留神。

第五条军规

留神初始化对性能的影响。

如果须要加载类,肯定要在 warmup 的阶段进行加载,除非你是想去测试加载的工夫。否则会对测试后果有影响。

同时也不要计算第一次 print 的工夫,因为 print 也会加载和初始化一些类。

第六条军规

要留神反优化和重编译的影响。

JIT 在上面的几个非凡的状况下,须要对代码进行返优化:

有些非凡的状况上面,的确是须要进行反优化的。

上面是比拟常见的状况:

  1. 须要调试的状况

如果代码正在进行单个步骤的调试,那么之前被编译成为机器码的代码须要反优化回来,从而可能调试。

  1. 代码废除的状况

当一个被编译过的办法,因为种种原因不可用了,这个时候就须要将其反优化。

  1. 优化之前编译的代码

有可能呈现之前优化过的代码可能不够完满,须要从新优化的状况,这种状况下同样也须要进行反优化。

重编译是指 JIT 可能会从新优化代码,导致从新编译。

所以这条规定要求咱们 warmup 的工夫要尽可能的长。以便让 JIT 充沛优化。

第七条军规

在应用 benchMark 得出结论之前,肯定要去认真的了解 JVM 的底层代码(Assembly code),找到其景象的实质。

千万不要激动的下结论。最好是应用可视化的工具来剖析。比如说 jitwatch。

最初一条军规

在测试的时候肯定要防止其余程序的影响。

比如说两次测试,第一次测试是单机运行,第二次测试是在有其余服务正在运行的状况下进行的。

很显然这两次的后果是不能做比拟的。咱们须要多运行,剔除乐音后果。

总结

把握下面几条规定,置信大家也可能写出属于本人的 Benchmarks。

本文链接:http://www.flydean.com/how-to-write-benchmarks/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

退出移动版