摘要:本文介紹了Visual C++ .NET 2003產品中提供的代碼最佳化功能。此外,對於那些還不熟悉 Visual C++ .NET 2002 中進行的改進的讀者,本文還用一個較短的部分介紹了在這一改進中引入的新的“全程式最佳化”功能。最後,本文討論了一些與最佳化有關的“最佳策略”,以及對 Visual C++ 編譯器進行的一般性增強。
簡介
雖然得到了一種新的工具,但對於自己是否以可能的最佳方式使用它沒有把握,這總是一件令人感到沮喪的事情。該白皮書試圖減少您對 Visual C++ 最佳化器可能具有的憂慮,從而使您確信自己正在最大限度地發揮它的作用。
Visual C++ .NET 2003
Visual C++ .NET 2003 版本增加了兩個新的與效能有關的編譯器選項,此外還包含了對 Visual C++ .NET 2002 中附帶的幾項最佳化的改進。
第一個新的與效能有關的選項是 /G7。該選項告訴編譯器針對 Intel Pentium 4 和 AMD Athlon 處理器最佳化代碼。
用 /G7 編譯應用程式獲得的效能改進各不相同,但與 Visual C++ .NET 2002 所產生的程式碼相比較,典型程式的執行時間減少 5% 到 10% 並不罕見,對於包含大量浮點代碼的程式而言,甚至可能減少 10% 到 15%。改進的範圍可能相差很大,在某些情況下,使用者如果用 /G7 編譯並且在最新一代處理器上運行,則會看到改進的幅度超過 20%。
使用 /G7 並不意味著編譯器將產生只能在 Intel Pentium 4 和 AMD Athlon 處理器上啟動並執行代碼。用 /G7 編譯的代碼仍然能夠在這些處理器的舊代產品中運行,儘管可能有一些小的效能損失。此外,我們已經注意到一些特殊的情況,即用 /G7 編譯時間產生了在 AMD Athlon 上運行速度更慢的代碼。
當未指定 /Gx 選項時,編譯器將預設使用 /GB,即“混合”最佳化模式。在 Visual C++ .NET 的 2002 版本和 2003 版本中,/GB 都等價於 /G6,也就是針對 Intel Pentium Pro、Pentium II 和 Pentium III 最佳化代碼。
在使用 /G7 時進行的改進的一個樣本是,在執行以常數為乘數的整數乘法時,更好地為 Intel Pentium 4 選擇指令。例如,以下面的代碼為例:
int i;
...
// Do something that assigns a value to i.
...
return i*15;
當用 /G6(預設選項)編譯時間,我們將產生以下指令:
mov eax, DWORD PTR _i$[esp-4]
imul eax, 15
當用 /G7 編譯時間,我們產生了更快(但更長)的指令序列,避免了使用 imul 指令,該指令在 Intel Pentium 4 上具有 14 個周期的延隔時間。
mov ecx, DWORD PTR _i$[esp-4]
mov eax, ecx
shl eax, 4
sub eax, ecx
第二個與效能相關的選項是 /arch:[參數],它採用參數 SSE 或 SSE2。該選項使編譯器可以利用 Streaming SIMD Extensions (SSE) 和 Streaming SIMD Extensions 2 (SSE2) 指令,以及其他在支援 SSE 和/或 SSE2 的處理器上提供的新指令。當用 /arch:SSE 編譯時間,產生的代碼將只能在支援 SSE 指令以及 CMOV、FCOMI、FCOMIP、FUCOMI 和 FUCOMIP 的處理器上運行。與此類似,當用 /arch:SSE2 編譯時間,產生的代碼將只能在支援 SSE2 指令的處理器上運行。
對於 /G7 而言,用 /arch:SSE 或 /arch:SSE2 編譯應用程式所獲得的效能改進是不同的。通常的改進是執行時間減少 2% 到 3%,儘管在某些罕見的情況下測量到執行時間減少了5% 以上。
/arch:SSE 選項具有以下特定效果:
| • |
為單精確度浮點 (float) 變數利用 SSE 指令 — 如果這樣能獲得效能改進。 |
| • |
利用 CMOV 指令 — 該指令最初是在 Intel Pentium Pro 處理器中引入的。 |
| • |
利用 FCOMI、FCOMIP、FUCOMI 和 FUCOMIP 指令 — 它們最初也是在 Pentium Pro 處理器中引入的。 |
/arch:SSE2 選項具有 /arch:SSE 選項的所有效果,並且還具有以下效果:
| • |
為雙精確度浮點 (float) 變數利用 SSE 指令 — 如果這樣能獲得效能改進。 |
| • |
為 64 位元移位利用 SSE2 指令。 |
除了上述好處以外,在將 /GL(“全程式最佳化”)選項與 /arch:SSE 或 /arch:SSE2 結合使用來進行產生時,編譯器將為具有浮點參數和傳回值的函數使用自訂呼叫慣例。
最後,在 Visual C++ .NET 2003 中,對在該產品的以前版本中引入的幾項最佳化進行了增強。其中一項增強是能夠消除傳遞“死”參數(那些未在被調用的函數中引用的參數)的情況。例如:
int
f1(int i, int j, int k)
{
return i+k;
}
int
main()
{
int n = a+b+c+d;
m = f1(3,n,4);
return 0;
}
在 function f1() 中,第二個參數從未使用。當用 /GL(“全程式最佳化”)選項編譯時間,編譯器將為 main() 中對 f1() 的調用產生與下面類似的指令序列:
mov eax, 4
mov ecx, 3
call ?f1@@YAHHHH@Z
mov DWORD PTR ?m@@3HA, eax
在該樣本中,永遠不會執行對“n”的值的計算,並且只有在 f1() 中引用的兩個參數被傳遞給 f1()(而且它們是在寄存器中傳遞,而不是在堆棧上傳遞)。同時,該樣本是在禁用內聯功能的情況下編譯的,因為如果啟用內聯功能,則該調用將被完全最佳化掉,而剩餘的代碼會將“m”設定為值 7。