我知道多態可以增加一個明顯的開銷。調用虛擬函數比調用非虛擬函數要慢。 (我所有的經驗是關於GCC,但我覺得/聽說,這是任何realcompiler如此。)C++:戰鬥多態開銷
很多時候,一個給定的虛函數被一遍又一遍地叫同一對象上;我知道,對象類型不改變,並且大多數時候編譯器可以很容易地扣除的具有良好:
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
我想只有-02,-O3和-Os是內聯Final::f
。
而這些測試已經在我的機器上運行,運行的是最新GCC和AMD速龍(TM)64 X2雙核4000+處理器的CPU。我想在一個更便宜的平臺上它可能會慢很多。
因此,我認爲你說你的代碼正在爬行緩慢,你分析它,發現問題是在多態性? – wilhelmtell 2010-11-06 02:30:32
如果'BaseType'中的'f'是虛擬的,並且'FinalType'是從BaseType派生的,那麼'FinalType'中的'f'也是虛擬的。 – 2010-11-06 02:33:13
另外。 'dynamic_cast <>()'在運行時有一個檢查的代價,多態的代價是一個單一的指針解引用。我建議,無論何時你說「開銷」這個詞,都要確保你完全**地說**這個開銷是多少,至少你第一次談論這個開銷。我們清楚我們在這裏想要消除什麼。所以,現在,我認爲你分析了這兩種方法,發現多態性比你的黑客更慢? – wilhelmtell 2010-11-06 02:35:44