peo*_*oro 8 c++ polymorphism optimization virtual-functions
我知道多态可以增加明显的开销.调用虚函数比调用非虚函数要慢.(我的所有经验都是关于GCC的,但我认为/听说过任何真正的编译器都是如此.)
很多时候,一个给定的虚函数被反复调用同一个对象; 我知道对象类型不会改变,并且大多数时候编译器可以很容易地推断出对象类型:
BaseType &obj = ...;
while( looping )
obj.f(); // BaseType::f is virtual
Run Code Online (Sandbox Code Playgroud)
为了加快代码,我可以像这样重写上面的代码:
BaseType &obj = ...;
FinalType &fo = dynamic_cast< FinalType& >( obj );
while( looping )
fo.f(); // FinalType::f is not virtual
Run Code Online (Sandbox Code Playgroud)
我想知道在这些情况下由于多态性而避免这种开销的最佳方法是什么.
高级构建的想法(如第二个片段所示)对我来说看起来并不好:BaseType
可以被许多类继承,并且尝试向上转换为所有这些将是非常冗长的.
另一个想法可能是存储obj.f
在一个函数指针(没有测试这个,不确定它会杀死运行时开销),但再次这个方法看起来并不完美:如上面的方法,它需要写更多代码,它将无法利用一些优化(例如:如果FinalType::f
是一个内联函数,它不会被内联 - 但我想避免这种情况的唯一方法是向上obj
转换为其最终类型.. .)
那么,还有更好的方法吗?
编辑: 嗯,当然这不会影响那么多.这个问题主要是要知道是否有事可做,因为看起来这个开销是免费的(这个开销看起来很容易被杀)我不明白为什么不这样做.
一个简单的关键字,用于小优化,如C99 restrict
,告诉编译器一个固定类型的多态对象是我所希望的.
无论如何,只是回答评论,存在一点开销.看看这个特殊的极端代码:
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;
}
Run Code Online (Sandbox Code Playgroud)
编译并运行它,需要时间:
$ 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
Run Code Online (Sandbox Code Playgroud)
我猜只有-O2,-O3和-Os是内联的Final::f
.
这些测试已在我的机器上运行,运行最新的GCC和AMD Athlon(tm)64 X2双核处理器4000+ CPU.我想在更便宜的平台上可能会慢得多.
如果动态调度是程序中的性能瓶颈,那么解决问题的方法就是不使用动态调度(不要使用虚函数).
您可以使用模板和泛型编程而不是虚函数来替换一些运行时多态与编译时多态.这可能会也可能不会带来更好的表现; 只有剖析器可以肯定地告诉你.
但要明确的是,正如wilhelmtell已经在对该问题的评论中指出的那样,由动态调度引起的开销很少值得担心.在用笨重的自定义实现替换内置的便利性之前,请确保它是您的性能热点.
归档时间: |
|
查看次数: |
2098 次 |
最近记录: |