Viusal C++.NET 2003 的优化代码

Viusal C++ 的优化代码

Mark Lacey

Microsoft Corporation

April 2003

翻译: cnss

概要:这篇文章介绍了 Visual C++.NET 2003 中的代码优化。另外,有些读者可能对 VC.NET 2002 的优化不太了解,所以我们会简短介绍一下全程优化 (Whole Program Optimization) 。最后我们用一些例子充分表现一下 VC.NET 的优化性能,并对其讨论。

本文适用于: Visual C++ .NET 2003

-------------------------------------------------------------------

** 前言 ** ** **

人们在使用一个新的编程工具时总会感到缺乏自信,本文试图让你对 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

<P class=MsoNormal style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; MARGIN: 0cm 0cm 0pt; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; mso-border-shadow: yes; mso-border-alt: solid windowtext 1.0pt;

Published At
Categories with Web编程
Tagged with
comments powered by Disqus