VC++.NET 2003代碼優化方法
概要: 這篇文章介紹了Visual C++.NET 2003中的代碼優化。另外,有些讀者可能對VC.NET 2002的優化不太了解,所以我們會簡短介紹一下全程優化(Whole Program Optimization)。最后我們用一些例子充分表現一下VC.NET的優化 性能 ,并對其討論。 前言 人們在使用
概要:這篇文章介紹了Visual
C++.NET 2003中的代碼優化。另外,有些讀者可能對VC
.NET 2002的優化不太了解,所以我們會簡短介紹一下全程優化(Whole Program Optimization)。最后我們用一些例子充分表現一下VC.NET的優化
性能,并對其討論。
前言 人們在使用一個新的
編程工具時總會感到缺乏自信,本文試圖讓你對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, FCO
MI, 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 ret 0 |
表現優化的最好例子 VC編譯器包括兩個主要的優化參數,"/O1"和"/O2"。"/O1"代表最小尺寸,選了它編譯器認為用了以下選項。
1./Og 全局優化,比如經常用到的變量使用寄存器保存,或者循環內的計算優化
2./Os 程序(exe或dll)尺寸優化優先于代碼速度優化
3./Oy 使用幀指針,以提高函數調用速度
4./Ob2 編譯器“覺得”應該使用內聯的函數,都使用內聯
5./GF 使用只讀字符串池
6./Gy 告訴編譯器將各個函數按打包格式編譯
"/O2"選項代表最快速度,它基本上與"/O1"相同,只是用"/Ot"(更快的代碼)代替了"/Os"。另外還有"/Oi"代表了展開內聯函數。
一般來說,對小程序使用最快優化,對大程序使用最小尺寸優化,這是因為尺寸大的程序通常能導致加載緩慢,CACHE命中率低,系統頻繁切換分布內存等問題。使用最小尺寸優化,編譯不再展開循環,也不會采用更長的代碼。
在選擇了主要優化選項后,用profile去尋找"熱區"是一個好辦法,這樣你可以對程序不同部分做最適當的優化。比如如果你用最小尺寸優化后,用profile發現有幾個函數執行的很頻繁,那你就可以把那幾個函數按最快速度優化。
VC編譯器可以對特定函數進行優化選項! 比如,如果你發現fiddle()函數被調用的頻率很高,那你就可以讓編譯器只對這個函數進行最快速度優化,這樣:
#pragma optimize("t", on) int fiddle(S *p) { …; } #pragma optimize("", on) |
除了"/O1"和"/O2"以外,還有"/Ox"選項,它很與"/O2"效果相同,而"/Ox"與"/Os"組合則與"/O1"效果相同。我們推薦使用"/O1"和"/O2",而不是用"/Ox"。
至此,我們討論了"/G7","/arch"和"/GL"優化選項。
除了上面介紹的,VC還提供了兩個:
1./GA 優化靜態線程局部存儲。(不要用于DLL project,用了也沒效果)
2./Gr 使用__fastcall作默認調用規則,這代表頭兩個參數會用寄存器傳送(如果參數能裝進寄存器)。
另外的一個選項是"/opt:ref",用它可以通知連接器,在連接時去掉沒被調用的函數和沒被使用的數據。用"/opt:icf"選項能合并相同函數(比如你的程序可能通過
模板展開了好幾遍),這時優化也能減小程序的尺寸。
Visual C++ .NET中的優化改進 這兒有3個重要的優化選項,你可以把它們用在VC.NET 2003的項目中。雖然VC.NET 2002也提供了這些選項,但VC.NET 2003對它們做了性能上的改進。
下表簡要的描述了它們,如果你想了解更詳細的內容,請查閱VC所帶的文檔。
選項 |
效果 |
/RTC1 |
使用無優化的Debug模式,編譯器插入動態檢測代碼以幫助你發現程序中的錯誤。比如你沒有初始化的內存,或者你把__stdcall和__cdecl弄混了。
|
/GS |
加入檢測靜態緩沖區(棧)溢出的代碼,黑客就不能覆蓋函數返回的地址以執行惡意代碼。 注意:這不意味著你可以高枕無憂,你仍要留心編寫安全的代碼! |
/Wp64 |
檢測生成64位代碼的問題,通過它你可以發現移植到64位環境下你的代碼可能出現的問題。 |
結論 VC.NET 2003引入了兩個新的優化選項,同時也改進了VC.NET 2002中的幾個優化的性能,希望你能通過VC.NET 2003的優化選項來提高你程序的
質量。
原文轉自:http://www.anti-gravitydesign.com
- 評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
-
国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97
|