繁体   English   中英

C ++:对抗多态性开销

[英]C++: fighting polymorphism overhead

我知道多态可以增加明显的开销。 调用虚函数比调用非虚函数要慢。 (我的所有经验都是关于GCC的,但我认为/听说过任何真正的编译器都是如此。)

很多时候,一个给定的虚函数被反复调用同一个对象; 我知道对象类型没有改变,并且大多数时候编译器可以很容易地推断出那个很好:

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

我想知道在这些情况下由于多态性而避免这种开销的最佳方法是什么。

高级转换的想法(如第二个片段所示)对我来说看起来不太好: 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;
}

编译并运行它,需要时间:

$ 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双核处理器4000+ CPU。 我想在更便宜的平台上可能会慢得多。

如果动态调度是程序中的性能瓶颈,那么解决问题的方法就是不使用动态调度(不要使用虚函数)。

您可以使用模板和泛型编程而不是虚函数来替换一些运行时多态与编译时多态。 这可能会也可能不会带来更好的表现; 只有剖析器可以肯定地告诉你。

但要明确的是,正如wilhelmtell已经在对该问题的评论中指出的那样,由动态调度引起的开销很少值得担心。 在用笨重的自定义实现替换内置的便利性之前,请确保它是您的性能热点。

如果您需要使用多态,那么使用它。 实际上没有更快的方法。

但是,我会回答另一个问题:这是你最大的问题吗? 如果是这样,您的代码已经是最佳的或几乎是最佳的。 如果没有,找出最大的问题,并专注于此。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM