前言
人们在使用一个新的编程工具时总会感到缺乏自信本文试图让你对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(全程优化)选项选项时编译器会对浮点参数和浮点返回值做函数调用规则优化
上面说的几点优化特性已经包括于VCNET 里了另外还有一点就是能消除死参数从没被用过的参数比如
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
VCNET 引入了全程优化(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
ret