【发布时间】:2011-05-05 22:09:39
【问题描述】:
我知道多态性会增加显着的开销。调用虚函数比调用非虚函数慢。 (我所有的经验都是关于 GCC,但我认为/听说这适用于任何 realcompiler。)
给定的虚函数在同一个对象上多次被调用;我知道对象类型不会改变,而且大多数时候编译器可以很容易地推断出来:
BaseType &obj = ...;
while( looping )
obj.f(); // BaseType::f is virtual
为了加快代码速度,我可以像这样重写上面的代码:
BaseType &obj = ...;
FinalType &fo = dynamic_cast< FinalType& >( obj );
while( looping )
fo.f(); // FinalType::f is not virtual
我想知道在这些情况下,由于多态性,避免这种开销的最佳方法是什么。
大写的想法(如第二个 sn-p 所示)对我来说看起来不太好:BaseType 可以被许多类继承,并且尝试对所有类进行大写是相当冗长。
另一个想法可能是将obj.f 存储在函数指针中(没有对此进行测试,不确定它是否会杀死运行时开销),但是这种方法看起来并不完美:与上述方法一样,它需要编写更多代码并且它无法利用一些优化(例如:如果FinalType::f 是一个内联函数,它不会被内联 - 但我想避免这种情况的唯一方法是将obj 转换为最终类型...)
那么,有没有更好的方法呢?
编辑: 好吧,当然这不会产生太大影响。这个问题主要是想知道是否有事可做,因为看起来这个开销是免费提供的(这个开销看起来很容易杀死)我不明白为什么不这样做。
一个用于小优化的简单关键字,例如 C99 restrict,告诉编译器多态对象是固定类型,这是我所希望的。
无论如何,只是为了回答 cmets,存在一点开销。看看这个临时 extreme 代码:
struct Base { virtual void f(){} };
struct Final : public Base { void f(){} };
int main( ) {
Final final;
Final &f = final;
Base &b = f;
for( int i = 0; i < 1024*1024*1024; ++ i )
#ifdef BASE
b.f( );
#else
f.f( );
#endif
return 0;
}
编译和运行它,花费时间:
$ for OPT in {"",-O0,-O1,-O2,-O3,-Os}; do
for DEF in {BASE,FINAL}; do
g++ $OPT -D$DEF -o virt virt.cpp &&
TIME="$DEF $OPT: %U" time ./virt;
done;
done
BASE : 5.19
FINAL : 4.21
BASE -O0: 5.22
FINAL -O0: 4.19
BASE -O1: 3.55
FINAL -O1: 1.53
BASE -O2: 3.61
FINAL -O2: 0.00
BASE -O3: 3.58
FINAL -O3: 0.00
BASE -Os: 6.14
FINAL -Os: 0.00
我猜只有 -O2、-O3 和 -Os 是内联 Final::f。
这些测试已经在我的机器上运行,运行最新的 GCC 和 AMD Athlon(tm) 64 X2 Dual Core Processor 4000+ CPU。我想在更便宜的平台上它可能会慢很多。
【问题讨论】:
-
所以,我认为您是在说您的代码爬行速度很慢,而您对其进行了分析,发现问题出在多态性上?
-
如果
f在BaseType中是虚拟的并且FinalType是从BaseType派生的,那么f在FinalType中也是虚拟的。 -
还有。
dynamic_cast<>()有运行时检查的代价,多态的代价是单指针解引用。我建议每当您说“开销”这个词时,请确保您确切地说出该开销是什么,至少在您第一次谈论该开销时。只是为了让我们清楚我们要在这里消除什么。所以,现在,我认为你分析了这两种方法,发现多态性比你的 hack 慢? -
@peoro:如果
FinalType::f的参数类型与BaseType::f的参数类型相同,那么FinalType::f是虚拟的,会覆盖BaseType::f。在派生类中是否使用关键字virtual声明函数并不重要。在您的示例中,由于f没有参数,因此FinalType::f会覆盖BaseType::f。 -
即使您知道 FinalType 始终是派生最多的类,编译器通常也很难或不可能知道这一点。虽然编译器可以优化一些虚函数调用并确定在编译时调用哪个函数,但在大多数情况下它不能。您在单个翻译单元中声明、定义和使用所有类的示例测试是编译器何时可以进行此优化的罕见示例。
标签: c++ optimization polymorphism virtual-functions