真相,看问题的层次

先看问题:

 
 
题目很简单吧,用C#执行pow(2)的性能比C++执行pow(2)的性能慢了一个数量级.大家先想想看.
 
我,Support Engineer:
 
从我这个support engineer的层次来说呢,二话不说先祭出reflector,profiler和windbg三大武器了.Reflector看到pow方法是InternalCall属性,所以猜想问题应该在C#和CLR Engine的context switch上。然后用profiler测了一把,果然,大量的时间消耗在了mscorwks上面。最后,用windbg随即breakin检查callstack,发现调用往往停留在
 
0:000> kL
ChildEBP RetAddr 
0013f3d8 7a296970 MSVCR80!_pow_pentium4+0x313
0013f480 79e88f63 mscorwks!COMDouble::PowHelper+0x8a
0013f510 79e88e31 mscorwks!CallDescrWorker+0x33
0013f650 79e88d19 mscorwks!MethodDesc::CallDescr+0x19c
0013f668 79e88cf6 mscorwks!MethodDesc::CallTargetWorker+0x20
0013f67c 79f084b0 mscorwks!MethodDescCallSite::Call_RetArgSlot+0x18
0013f7e0 79f082a9 mscorwks!ClassLoader::RunMain+0x220
0013fa48 79f0817e mscorwks!Assembly::ExecuteMainMethod+0xa6
0013ff18 79f07dc7 mscorwks!SystemDomain::ExecuteMainMethod+0x398
0013ff68 79f05f61 mscorwks!ExecuteEXE+0x59
 
好吧,有了三个铁的证据,C#的代码通过InternalCall把调用传递给了CRT。C++也是通过 CRT调用。唯一的差别就是C#通过InternalCall进去的,那问题就在这个context switch上了
 
Developer:
 
我把这个问题发到内部讨论组上后,一个developer先回我了。大意是,乃不就计算一个平方么,用什么 pow函数阿,直接用乘法不是更快么?
 
修改代码,用乘法替换pow后,C#跟C++性能就一样了。
 
看到了吧,用什么reflector, windbg,代码本身就在乱掉函数,如果代码写得对,这个问题就不存在!
 
回到我:
 
可是我还是死脑精啊。用乘法的确快,但是我一定要用pow呢,有没有办法让C#快点啊!

SOFTWARE ARCHITECT:
 
这个时候,一个SOFTWARE ARCHITECT跳出来了。列出四点:

1. 你说问题出在context switch,这点很值得怀疑
2. 如果jit真的编译除了效率不高的代码,我们应该找jit的原因
3. C++编译器优化可以自动把pow转换成乘法,jit好像不会做这么复杂的工作
4. 事实上是,C#做出来的工程,哪怕在复杂计算方面,跟C++相比也非常有竞争力
 
回到我:
 
ARCHITECT说得很有道理啊。于是我重新拿windbg开始分析。果然,问题不在InternalCall上。问题在于:
 
1. pow函数接受的参数定义为double,也就是说pow可以做浮点指数运算。浮点指数运算比整数指数运算复杂多了,如果用CPU的一些优化,比如MMX,可以提高效率
2. C++看到pow(2),直接优化为乘法,同时还inline,避免调用浮点指数运算来提高效率
3. jit看到internalcall,简单判断后直接生成对CRT的调用,所以就没有优化为乘法这一步.最后就把这个2当成浮点数在计算,所以慢了
4. 察看InternalCall的调用,其实就是一个简单的call,根本没有什么context switch.
 
为了印证上面的说法,把pow(2)修改为pow(2.5),测试下来,C#比C++还快了5秒!赶紧认错去.
 
看到了吧,我跟ARCHITECT的层次差了多少

Senior Developer:
 
故事才刚开始,一个Senior Developer看到了,给出一些链接,讨论了CLR,Java这样的动态编译优化技术,同时也有人建议CLR可以带一个跟C++编译器一样,虽然比较大,但是能够尽可能优化代码的编译引擎出来
 
ARCHITECT:
 
另外一个CLR Team的CLR于是出现了.说CLR的确考虑过带一个这样的编译引擎的.但是考虑到下面两点,在目前的版本上不会实现这样的引擎:
 
1. 目前有专门的研究团队在研发这样的通用优化技术.这个技术出来后,CLR就可以使用
2. 在当前情况下引进复杂的编译引擎开销太大
 
PRINCIPAL RESEARCHER:
 
这个时候,研究院的PRINCIPAL RESEARCHER出来了,介绍了一下当前该项技术的进展.当前研究的这项技术是非常抽象的,研究出来后可以直接用于各种编译环境,到时候C++编译器都会用这个技术重写.目前进展不错,前景可观,但是难题也不少
 
GENERAL MANAGER:
 
在PRINCIPAL RESEARCHER的激情介绍后,一个管研发的GENERAL MANAGER出来了,说你这个优化技术2001年就承诺大家可以出来了,到现在都CLR3.0了还不出来,怎么回事呢?
 
Senior ARCHITECT
 
负责优化的Senior ARCHITECT出来说话了,说这个技术目前在实验室里面已经可以用于CLR了。01年研究团队只有3个人,现在有70多个人了。这个技术非常重要,有非常长远的考虑和影响,考虑到诸多因素,所以需要非常谨慎地进行

......

在这后面,又有无数的RESEARCHER,SDET Lead, ARCHITECT, Senior Programmer出来,讨论这个优化技术的进展。具体内容就不在这里讨论了。总之呢,不同的人,看问题的态度和高度是灰常不一样的。
 
你我还错过了多少真相?
posted @ 2009-06-18 00:06  h-hello  阅读(239)  评论(0编辑  收藏  举报