前言
人們在使用一個新的編程工具時總會感到缺乏自信,本文試圖讓你對VC的代碼優化有更直觀的感覺,希望你能通過閱讀本文從VC中"得到"更多的東西。
Visual C++ .Net 2003
VC.NET 2003不僅帶來了兩個新的優化選項,它還改進了VC.Net 2002中一些優化的性能。
第一個新增選項是"/G7",它告訴編譯器對Intel Pentium 4和AMD Athlon處理器進行優化。
使用"/G7"選項編譯的程序,當我們和VC.Net 2002生成的代碼比較時發現,它通常能使典型的程序的運行速度提高5到10個百分點,如果使用了大量浮點代碼甚至能提高10到15個百分點。而提高的優化程度可能很高也可能較低,在一些使用最新CPU和"/G7"選項的測試中,甚至提高了20%的性能。
使用"/G7"選項不代表生成的代碼只能運行在Intel Pentium 4和AMD Athlon處理器上。這些代碼仍可以運行在老的CPU上,只是在性能表現上可能有"小小的懲罰".另外,我們觀察到一些程序使用"/G7"後在AMD Athlon上運行的比用Intel Pentium 4更慢。
當沒使用"/Gx"選項時,編譯器會默認使用"/GB"選項,此時為"blended"優化模式。在VC.NET 2002和VC.Net 2003中,"/GB"代表"/G6",即為Intel Pentium Pro, Pentium II, Pentium III處理器優化。
這兒有一個例子,它展示了做與常整數乘法時使用Pentium 4和"/G7"的優化效果,下面是源代碼:
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(乘)指令,在Pentium 4上執行只需要14個周期。目標代碼如下:
mov ecx, DWord PTR _i$[esp-4]
mov eax, ecx
shl eax, 4
sub eax, ecx
第二個優化選項是"/arch:[argument]",用它可對SSE或SSE2優化,生成使用Streaming SIMD Extensions (SSE) 和 Streaming SIMD Extensions 2 (SSE2) 指令集的程序。當使用"/arch:SSE"選項時,目標代碼只能運行在支持SSE指令(如:CMOV, FCOMI, FCOMIP, FUCOMI, FUCOMIP)的CPU上。當使用"/arch:SSE2"選項時,目標代碼只能運行在支持SSE2指令集的CPU上。
相比於"/G7",使用了SSE或SSE2優化的程序,一般能減少2-3%的運行時間,個別測試中甚至能減少5%的運行時間。
使用"/arch:SSE"可得到以下效果:
1。在使用單精度浮點數時,使用SSE指令對其處理。
2。使用CMOV指令,它最早被Pentium Pro支持。
3。使用FCOMI, FCOMIP, FUCOMI, FUCOMIP指令,它們也是最早被Pentium Pro支持的。
使用"/arch:SSE2"的話,可以得到所有"/arch:SSE"選項的效果,另外還有以下幾個效果:
1。在使用雙精度浮點數時,使用SSE2指令對其處理。
2。使SSE2指令集做64位切換。(原文:Making use of SSE2 instructions for 64-bit shifts)
還有其它的好處,在同時使用"/arch:SSE"或"/arch:SSE2” 和 "/GL"(全程優化)選項選項時,編譯器會對浮點參數和浮點返回值做函數調用規則優化。
上面說的幾點優化特性已經包括於VC.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;
}
在函數f1()中,第二個參數從沒被使用過。當我們用"/GL"(全程優化)選項時,編譯器將產生如下目標代碼來調用f1():
mov eax, 4
mov ecx, 3
call ?f1@@YAHHHH@Z
mov DWord PTR ?m@@3HA, eax
在這個例子裡,變量"n"從沒被運算,只有兩個參數被f1()使用,所以只傳遞那兩個參數(並且它們是從寄存器傳過去的,這比使用棧傳更快)。另外,編譯這個例子時要禁止內聯(inlining),否則函數f1()就不存在了,而直接給m賦予值7。
Visual C++ .Net 2002
VC.Net 2002引入了全程優化(Whole Program Optimization,縮寫為WPO)的概念,"/GL"選項代表使用全程優化。全程優化意味著:編譯器在.obj文件中存放的是代碼的中間表達而不是目標代碼,在連接時連接器對其優化處理並生成真正的目標代碼。
全程優化的一個主要好處在於我們可以跨越源文件進行函數內聯,這將大大提高程序的性能。還有一個好處在於編譯器可以跟蹤內存和寄存器的使用,以便優化使函數調用的開銷更小。
下面的代表展示了全程優化的表現:
// File 1
extern void func (int *, int *);
int g, h;
int
main()
{
int i = 0;
int j = 1;
g = 5;
h = 6;
func(&I, &j);
g = g + i;
h = h + i;
return 0;
}
// File 2
extern int g;
extern int h;
void
func(int *pi, int *pj)
{
*pj = g;
h = *pi;
}
當不使用"/GL"選項時,生成了如下代碼:
sub esp, 8
lea eax, DWord PTR _j$[esp+8]
push eax
lea ecx, DWord PTR _i$[esp+12]
push ecx
mov DWord PTR _i$[esp+16], 0
mov DWord PTR _j$[esp+16], 1
mov DWord PTR ?g@@3HA, 5
mov DWord PTR ?h@@3HA, 6
call ?func@@YAXPAH0@Z
mov eax, DWord PTR _i$[esp+16]
mov edx, DWord PTR ?g@@3HA
mov ecx, DWord PTR ?h@@3HA
add edx, eax
add ecx, eax
mov DWord PTR ?g@@3HA, edx
mov DWord PTR ?h@@3HA, ecx
xor eax, eax
add esp, 16
ret 0
當使用了"/GL"時,你會看到下面的代碼,現在的代碼短多了。注意編譯這個例子時同樣要注意關掉內聯優化。
sub esp, 8
lea ecx, DWord PTR _j$[esp+8]
lea edx, DWord PTR _i$[esp+8]
mov DWord PTR _i$[esp+8], 0
mov DWord PTR ?g@@3HA, 5
mov DWord PTR ?h@@3HA, 6
call ?func@@YAXPAH0@Z
mov DWord PTR ?g@@3HA, 5
xor eax, eax
add esp, 8