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