[英]using virtual functions versus static_cast from base to derived
我試圖了解下面哪個實現“更快”。 假設使用和不使用-DVIRTUAL標志編譯此代碼。
我假設沒有-DVIRTUAL的編譯會更快,因為:
a]沒有使用vtable
b]編譯器可能能夠優化匯編指令,因為它“確切地”知道在給定各種選項的情況下將進行哪個調用(只有有限數量的選項)。
我的問題是PURELY與速度有關,而不是漂亮的代碼。
a]我在上面的分析中是否正確?
b]分支預測器/編譯器組合是否足夠智能以優化switch語句的給定分支? 看到“type”是一個const int。
c]我還缺少其他因素嗎?
謝謝!
#include <iostream>
class Base
{
public:
Base(int t) : type(t) {}
~Base() {}
const int type;
#ifdef VIRTUAL
virtual void fn1()=0;
#else
void fn2();
#endif
};
class Derived1 : public Base
{
public:
Derived1() : Base(1) { }
~Derived1() {}
void fn1() { std::cout << "in Derived1()" << std::endl; }
};
class Derived2 : public Base
{
public:
Derived2() : Base(2) { }
~Derived2() { }
void fn1() { std::cout << "in Derived2()" << std::endl; }
};
#ifndef VIRTUAL
void Base::fn2()
{
switch(type)
{
case 1:
(static_cast<Derived1* const>(this))->fn1();
break;
case 2:
(static_cast<Derived2* const>(this))->fn1();
break;
default:
break;
};
}
#endif
int main()
{
Base *test = new Derived1();
#ifdef VIRTUAL
test->fn1();
#else
test->fn2();
#endif
return 0;
}
我想你誤解了VTable。 VTable只是一個跳轉表(在大多數實現中,盡管AFAIK規范並不能保證這一點!)。 事實上,你可以說它是一個巨大的轉換聲明。 因此,我敢打賭速度與你的兩種方法完全相同。
如果有什么我認為VTable方法會稍快一點,因為編譯器可以做出更好的決策來優化緩存對齊等等......
你有沒有測量過表現,看看是否有任何差別?
我想不是,因為那樣你就不會在這里問。 這是唯一合理的回應。
如果不指定編譯器和編譯器選項,則無法回答。
我認為沒有什么特別的理由說明為什么非虛擬代碼必須比虛擬代碼更快地進行調用。 事實上,交換機可能比vtable慢,因為使用vtable的調用將加載一個地址並跳轉到它,而交換機將加載一個整數並做一些思考。 他們中的任何一個都可以更快。 由於顯而易見的原因,標准未指定虛擬調用“比您發明的任何其他更換它的速度慢”。
我認為隨機選擇的編譯器在虛擬情況下實際內聯調用是不太可能的,但它肯定允許(在as-if規則下),因為*test
的動態類型可以通過數據流分析來確定或類似的。 我認為,通過優化啟用,隨機選擇的編譯器可以內聯非虛擬情況下的所有內容。 但是,你在一個TU中給出了一個功能很短的小例子,所以內聯特別容易。
假設您沒有過早地進行微觀優化,並且您已經分析了代碼並發現這是一個需要解決的問題,那么找出問題答案的最佳方法是在發布中進行完全優化並檢查生成的機器代碼。
避免vtable的速度不一定是真的 - 確定,你應該衡量自己。
注意:
static_cast
版本可能會引入一個分支(如果它被優化為跳轉表,則可能不會) vtable
版本都會產生一個跳轉表。 看到這里的模式?
通常,您更喜歡線性時間查找,而不是分支代碼,因此虛函數方法似乎更好。
這取決於平台和編譯器。 switch
語句可以實現為測試和分支或跳轉表(即間接分支)。 virtual
函數通常實現為間接分支。 如果編譯器將switch
語句轉換為跳轉表,則這兩種方法會有一個額外的解引用。 如果是這種情況並且這種特殊用法不經常發生(或者足夠多地使緩存崩潰),那么由於額外的緩存未命中,您可能會看到差異。
另一方面,如果switch
語句只是一個測試和分支,您可能會看到在某些有序CPU上的更大性能差異,這些CPU在間接分支上刷新指令緩存(或者在設置目標之間需要高延遲)間接分支並跳轉到它)。
如果您真的關心虛函數調度的開銷,比如說,對於異構對象集合的內循環,您可能需要重新考慮執行動態調度的位置。 它不一定是每個對象; 它也可以是每個已知的具有相同類型的對象分組。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.