在 VC.NET 使用預設 /clr 編譯時,一個托管函式會產生兩個入口點:一個供托管程式碼呼叫,另一個供非托管程式碼呼叫。但函式位址(特別是虛函式指標)只能有一個值,因此編譯器必須選擇一個預設入口點。
在 Visual Studio .NET 2003 中,編譯器總選擇非托管入口點作為預設;而在 Visual Studio 2005 中,若參數或傳回值包含托管類型,則選擇托管入口點。另一個入口點則透過互操作功能轉發呼叫。當托管函式互相呼叫時,這可能導致不必要的托管/非托管上下文切換與參數/傳回值複製,影響效能。
若函式不會被非托管程式碼透過指標呼叫,可在宣告時加上 VC2005 新增的 __clrcall 修飾符,阻止編譯器產生雙重入口點,強制僅使用托管呼叫慣例,從而消除上下文切換開銷。
以下程式碼比較一般虛函式與 __clrcall 虛函式在氣泡排序中的效能差異:
using namespace System;
#define ARRAY_SIZE 1000
struct bubbleBase { int value; };
class bubble1 : public bubbleBase {
public:
virtual int getvalue() { return value; }
virtual void setvalue(int newvalue) { value = newvalue; }
};
class bubble2 : public bubbleBase {
public:
virtual int __clrcall getvalue() { return value; }
virtual void __clrcall setvalue(int newvalue) { value = newvalue; }
};
template<class T> void bubbleSort(int length) {
TimeSpan ts;
T* array1 = new T[ARRAY_SIZE];
for (int i = 0; i < ARRAY_SIZE; i++)
array1[i].setvalue(ARRAY_SIZE - i - 1);
Int64 ticks = DateTime::Now.Ticks;
int i, j, temp, test;
for (i = length - 1; i > 0; i--) {
test = 0;
for (j = 0; j < i; j++) {
if (array1[j].getvalue() > array1[j+1].getvalue()) {
temp = array1[j].getvalue();
array1[j].setvalue(array1[j+1].getvalue());
array1[j+1].setvalue(temp);
test = 1;
}
}
if (test == 0) break;
}
ts = TimeSpan::FromTicks(DateTime::Now.Ticks - ticks);
Console::WriteLine("BubbleSort {0} Items: {1} Ticks", ARRAY_SIZE, ts.Ticks);
delete array1;
}
int main(array<System::String^>^ args) {
bubbleSort<bubble1>(ARRAY_SIZE);
bubbleSort<bubble2>(ARRAY_SIZE);
return 0;
}
BubbleSort 1000 Items: 3281250 Ticks
BubbleSort 1000 Items: 312500 Ticks
結果顯示,加上 __clrcall 後效能提升超過 10 倍,顯著減少了托管代碼內部呼叫的開銷。
VC8.0 附帶的 STL 加入許多安全檢查,會降低執行速度。若確定程式無緩衝區溢位或記憶體越界風險,可定義 _SECURE_SCL=0 關閉此特性以提升效能。