VC++.NET 2003代碼優化方法

發表于:2007-05-25來源:作者:點擊數: 標簽:代碼方法優化VC++.NET2003
概要: 這篇文章介紹了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, 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
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