代码改变世界

c++ 虚函数机制 效率问题

2011-04-21 11:39  Rollen Holt  阅读(2283)  评论(0编辑  收藏  举报

原文:http://blog.csdn.net/metalkittie/archive/2008/11/12/3281916.aspx

今天在c++坛子里瞎逛,看到精华坛里在讨论“为什么虚函数效率低”的问题,
××楼主回答面试官说“跟cpu流水线执行效率有关”        
××某人回答“因为虚函数需要一次间接的寻址... 而一般的函数可以在编译时定位到函数的地址,虚函数(动态类型调用)是要根据某个指针定位到函数的地址. ” 
×ד虚函数有个虚函数表,而且会传一个index索引~!会间接寻址!”
×ד流水线执行的话,和"命中率"有关吧. 也就是说在流水线后端,已经译码成功的,和正在执行的代码的后继是一样的. 否则流水线会中断,也就是说在后端做的是无效的,需要重新译码.”    
搞笑的是以下人的回复:
×ד的确,计算机程序效率说到底和计算机指令流水线息息相关(还和缓存命中率有关)。但是,把虚函数效率低的原因解释到流水线这一层,是极其变态的,这个考官很可能是在卖弄自己的水平而已。”   
×ד楼主以后你要是遇到这种考官,你和他谈与非逻辑门,硅锗原子的组成和爱因斯坦相对论对虚函数的影响,绝对震惊四座!”
×ד说是因为流水线执行的原因,根本与问题不着边际。或者应该说影响流水线执行是效率低的无数原因中的一种才好。”    
×ד首先是由this指向查找虚函数表,然后找到相应的虚函数地址 
比非虚函数多查找一次 
如果是(多继承)基类指针指向派生类对象的话,有可能会涉及this指针的调整                     
比如先访问基类的成员数据再访问派生类的析构函数  就要进行一次this指针的调整 
具体可以参见 insied the c++ object model的多重继承下的virtual functions ”
×ד一些C++的书籍有明确的说明,针对类的虚函数的机制,如果有虚函数的话,编译器会为类增加一个虚函数表(VBL),当在动态执行程序时,会到该虚函数表中寻找函数。多增加了一个过程,效率肯定会低一些,但带来了运行时的多态。”    
×ד流水线 貌似说的是 CPU执行代码的提前取指令吧 
虚函数 效率低 是因为 执行过程中会跳转两次(首先找到对象的函数表,其次通过该函数表中存的虚函数表地址找到真正的执行地址),这样CPU运行的时候会跳转两次,而普通函数只跳一次。CPU每跳转一次,预取指令基本上就要作废很多,所以效率会很低。”
/////////////////////////////////////最后得分者
和流水线相关是说得通的,究其原因还是因为存在动态跳转,这会导致分支预测失败,流水线排空。 

设想一下,如果说不是虚函数,那么在编译时期,其相对地址是确定的,编译器可以直接生成jmp/invoke指令; 
如果是虚函数,多出来的一次查找vtable所带来的开销,倒是次要的,关键在于,这个函数地址是动态的,譬如 
取到的地址在eax里,则在call eax之后的那些已经被预取进入流水线的所有指令都将失效。流水线越长,一次分支预测失败的代价也就越大。 

pf->test(); 
011E146D  mov        eax,dword ptr [pf] 
011E1470  mov        edx,dword ptr [eax] 
011E1472  mov        esi,esp 
011E1474  mov        ecx,dword ptr [pf] 
011E1477  mov        eax,dword ptr [edx] 
011E1479  call        eax  <------------------------- 分支预测失效 
011E147B  cmp        esi,esp 
011E147D  call        @ILT+355(__RTC_CheckEsp) (11E1168h)    

此兄接着回答道“说到流水线,penalty基本上都是因为气泡(也就是分支指令造成预取失效),知道这个以后碰到了就不会再卡壳了。虽然引入流水线(流水线其实是 RISC最初使用的),极大提高了效率,流水线不是越长越好。像P4,几十级流水线,频率虽高,但是性能不好,很大原因就是因为流水线实在臭长。有兴趣可 以去看看CPU怎么做分支预测,乱序执行的。”
//////////////////////////////////
还是贴上原帖的地址吧 http://topic.csdn.net/u/20081031/12/06d0e218-8aab-4203-850c-9e6b76099c09.html
由此还引申出一个问题 虚函数在编译器里是怎么工作的