Java理论与实践: 动态编译与性能测量 - 编程入门网
e d) {
return d + 0.5 + 0.5;
}
}
public static class RoundaboutAdder implements Operator {
public double operate(double d) {
return d + 2.0 - 1.0;
}
}
public static void runABunch(Operator op) {
long start = System.currentTimeMillis();
double d = 0.0;
for (int i = 0; i < 5000000; i++)
d = op.operate(d);
long end = System.currentTimeMillis();
System.out.println("Time: " + (end-start) + " ignore:" + d);
}
public static void main(String[] args) {
Operator ra = new RoundaboutAdder();
runABunch(ra); // misguided warmup attempt
runABunch(ra);
Operator sa = new SimpleAdder();
Operator da = new DoubleAdder();
runABunch(sa);
runABunch(da);
}
}
StupidMathTest 首先试图做些预热(没有成功),然后测量 SimpleAdder、 DoubleAdder、 RoundaboutAdder 的运行时间,结果如表 2 所示。看起来好像 先加 1,再加 2 ,然后再减 1 最快。加两次 0.5 比加 1 还快。这有可能么? (答案是:不可能。) 表 2. StupidMathTest 毫无意义且令人误解的结果
这里发生什么呢?在预热循环之后, RoundaboutAdder和runABunch() 确实 已经被编译了,而且编译器 Operator和RoundaboutAdder 上进行了单形调用转 换,第一轮运行得非常快。而在第二轮( SimpleAdder)中,编译器不得不反优化 ,又退回虚函数分配之中,所以第二轮的执行表现得更慢,因为不能把虚函数调 用优化掉,把时间花在了重新编译上。在第三轮( DoubleAdder)中,重新编译比 第二轮少,所以运行得就更快。(在现实中,编译器会在 RoundaboutAdder和 DoubleAdder 上进行常数替换(constant folding),生成与 SimpleAdder 几乎 相同的代码。所以如果在运行时间上有差异,那么不是因为算术代码)。哪个代 码首先执行,哪个代码就会最快。 那么,从这个“评测”中,我们能得出什么结论呢?实际上,除了评测动态 编译语言要比您可能想到的要微妙得多之外,什么也没得到。 结束语 这个示例中的结果错得如此明显,所以很清楚,肯定发生了什么,但是更小 的结果能够很容易地歪曲您的性能测试程序的结果,却不会触发您的“这里肯定 有什么东西有问题”的警惕。虽然本文列出的这些内容是微评测歪曲的一般来源 ,但是还有许多其他来源。本文的中心思想是:您正在测量的,通常不是您以为 您正在测量的。实际上,您通常所测量的,不是您以为您正在测量的。对于那些 没有包含什么实际的程序负荷,测试时间不够长的性能测试的结果,一定要非常 当心。 |
||||||||
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |