转:健壮的 Java 基准测试
上一篇 / 下一篇 2012-09-14 17:23:52 / 个人分类:性能测试
健壮的 Java 基准测试,第 1 部分: 问题
51Testing软件测试网,x^2Bn;YO6B了解 Java 代码基准测试的问题
/g&i0Vzf#`X0VPG*@m$Qy ^0
p4W|2GcT/x8\x-q3l$E0简介:程序性能一直是受到关注的问题,即使在现在这样的高性能硬件时代,也是如此。本文是分两部分的文章系列的第一篇,讨论与 Java™ 代码基准测试相关的许多问题。第 2 部分讨论基准测试的统计并提供一个执行 Java 基准测试的框架。因为几乎所有新语言都是基于虚拟机的,所以本文讨论的基本原则适用于许多编程语言。
Xd*y9?/E0查看本系列更多内容51Testing软件测试网(|pG5fm(u:jD/U
51Testing软件测试网V|W!~9{51Testing软件测试网-`R-ZL@z$zR@
当今的 CPU 速度已经达到数 GHz,出现了多核处理器和数 GB 的内存,即使在这样的时代,程序性能问题仍然受到持续的关注。随着硬件功能的每次提高,都会出现具有挑战性的新型应用程序(或者增加了程序员的 “惰性”)。基准测试代码(以及从基准测试结果得出正确的结论)总是存在问题和困难,而且几乎没有比 Java 更难进行基准测试的语言,尤其是在先进的现代虚拟机上。
5JV _&^^2Wu.g \09OL | [{&g0m&Q5O0这个分两部分的文章系列只讨论程序执行时间,不考虑执行程序时的其他重要性质,比如内存使用量。即使在如此狭义的性能定义之下,精确地进行代码基准测试仍然有很多困难。这些问题的数量和复杂性使大多数基准测试都不太精确,常常导致误解。本文的第一部分只讨论这些问题,并给出了在编写自己的基准测试框架时需要考虑的各个方面。
8Ws2z7b t,qV/iB0c0[gMw*F ]*H0我首先通过一个性能难题演示一些基准测试方面的问题。请考虑清单 1 中的代码(参见参考资料获得本文的完整示例代码链接):
2SQ[U!|A&AK051Testing软件测试网 oC,w{ lF!bi清单 1. 性能难题51Testing软件测试网"U/M+e8\qt
protected static int global; public static void main(String[] args) { long t1 = System.nanoTime(); int value = 0; for (int i = 0; i < 100 * 1000 * 1000; i++) { value = calculate(value); } long t2 = System.nanoTime(); System.out.println("Execution time: " + ((t2 - t1) * 1e-6) + " milliseconds"); } protected static int calculate(int arg) { //L1: assert (arg >= 0) : "should be positive"; //L2: if (arg < 0) throw new IllegalArgumentException("arg = " + arg + " < 0"); global = arg * 6; global += 3; global /= 2; return arg + 2; } |
R F[ A$?9\$Q V0以下哪个版本运行得最快呢?
1zd.Iv1W,|6F }$C/J0- 保持此代码不变(
calculate
中没有arg
测试) - 只取消
L1
行的注释标志,但是禁用断言(使用-disableassertions
JVM 选项;这也是默认行为) - 只取消
L1
行的注释标志,但是启用断言(使用-enableassertions
JVM 选项) - 只取消
L2
行的注释标志
您至少应该猜出 A 版本(没有测试)一定是最快的,还可能猜到 B 应该与 A
差不多一样快,因为在关闭断言的情况下,L1
行是死代码,良好的动态优化编译器应该会消除它。这种猜测对吗?不幸的是,可能错了。清单 1中的代码取自 Cliff Click 在 2002 JavaOne 上的发言稿(参见参考资料)。他的幻灯片报告了下面的执行时间:51Testing软件测试网O Ku2c P
- 5 秒
- 0.2 秒
- (他没有报告这种情况下的数据)
- 5 秒
kDC9p8w*waQ)G0当然,最让人吃惊的是 B。它怎么会比 A 快 25 倍呢?51Testing软件测试网/{"{4e5o)m,_
6 年后,我在下面的现代配置上运行了清单 1中的代码(除非另外说明,本文中的所有基准测试结果都采用这种配置):51Testing软件测试网8B!f?@;R |Ym.A
- 硬件:2.2 GHz Intel Core 2 Duo E4500,2 GB RAM
- 操作系统:Windows® XP SP2,包含到 2008 年 3 月 13 日为止的所有更新
- JVM:1.6.0_05,所有测试都使用
-server
选项
我得到了以下结果:
H"]A!x'D*o*g?0- 38.601 ms
- 56.382 ms
- 38.502 ms
- 39.318 ms
B 现在明显比 A、C 和 D 慢。但是,结果仍然很奇怪:B 应该与 A 差不多,可是它比 C 还慢,这很让人吃惊。注意,我对每个版本做了 4 次度量,都获得了大体类似的结果(偏差在 1 ms 之内)。
ZgM.aDft%t$o8d%@07~M'h(L ]O'j8?R'M0Click 的幻灯片讨论了为什么会得到奇怪的结果(他把这种现象归因于复杂的 JVM 行为;还牵涉到一个 bug)。Click 是 HotSpot JVM 的架构师,所以他的解释应该是合理的。但是,普通的程序员有办法进行正确的基准测试吗?
f(E;G @1gsN"xq0答案是肯定的。在本文的第 2
部分中,我将提供一个 Java
基准测试框架,您可以放心地下载和使用它,因为它处理了许多基准测试问题。这个框架很容易满足大多数基准测试需求:只要把目标代码打包成特定类型的任务对象(Callable
或Runnable
),然后调用Benchmark
类。其他所有工作(性能度量、统计数据计算和结果报告)都会自动完成。