gpt4 book ai didi

C++:对抗多态开销

转载 作者:可可西里 更新时间:2023-11-01 17:39:46 25 4
gpt4 key购买 nike

我知道多态性会增加明显的开销。调用虚函数比调用非虚函数慢。 (我所有的经验都是关于 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

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

upper-casting 的想法(如第二个片段所示)对我来说看起来不太好:BaseType 可以被许多类继承,并尝试对所有类进行 upper-cast它们会非常冗长。

另一个想法可能是将 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 已经在对该问题的评论中指出的那样,动态调度引起的开销很少会大到足以让人担心的地步。在用笨拙的自定义实现替换内置的便利性之前,请绝对确保它是您的性能热点。

关于C++:对抗多态开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4111563/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com