您的位置:首页 > 运维架构 > 网站架构

在Intel 架构上释放程序的最佳性能

2008-01-28 22:17 127 查看
这篇文章是2004年发在程序员上的,今天居然在论坛上看到的, 写的不好,不过还是存在这儿。

软件性能的优化作为一项费时、艰难的工作往往被视为软件开发高手的领地,令一般软件开发人员望而却步。但是,软件性能对软件产品在市场中是否具有竞争力,以至软件产品能否取得成功,都起着关键作用。所以,如何提高软件的性能是作为软件工程师经常遇到的问题,也是软件工程师们一个头疼的问题。
有没有一种简单的方法可以提高软件的性能呢?Intel的软件开发工具为您提供了这种便利。灵活的使用Intel软件开发工具,您可以轻松,快捷的提高程序的性能,使程序在Intel架构上达到最佳性能。
从本期开始我们将推出一系列的文章来介绍如何使用Intel软件开发工具优化程序的性能。在阅读了这一系列文章后,您将可以通过使用Intel的软件开发工具释放出您的程序在Intel架构上的最佳性能。
作为这一系列文章的第一篇,我们将介绍如何使用Intel编译器优化程序的性能。

如何使用Intel编译器优化您程序的性能
编译器是当今软件开发中最基本的工具。编译器的性能直接影响生成的可执行程序的性能。最快捷,最简单改善程序性能的方法是使用具有优化功能的编译器。近年来编译器的优化功能取得了长足进展。一个好的编译器可以帮助你充分利用新型处理器的特性,使优化工作自动化,你将不必去翻厚厚的处理器手册。Intel编译器作为其中的佼佼者,充分利用了Intel 32位处理器和Intel 64位处理器的特性,可以使编译出的代码在其上的运行效率达到最高,是您在开发基于IA32(Intel32 位架构)和IA64(Intel64 位架构)上程序时的首选。
我们首先介绍了如何在Microsoft Visual C++开发环境中使用Intel编译器,并演示了如何用Intel C++编译器做针对特定Intel处理器的优化和如何编写适用于特定Intel处理器的函数,在文章的最后我们将讨论如何利用Intel C++使用Intel 处理器的SIMD 指令提高程序的性能。

1 使用Intel C++ 编译器
Intel C++编译器具有众多优化特性,这些特性充分利用了最新处理器的特性和先进的优化策略。而且它可以方便的集成到流行的集成开发环境中去,和其他开发工具协作完成开发工作。
以下我们讲解了如何在流行的C++开发工具Microsoft Visual C++中使用Intel C++编译器。在Intel C++编译器安装完成后, 它将自动集成到Microsoft Visual C++开发环境中。
在Microsoft Visual C++ 6.0 中,通过在Microsoft Visual C++ 6.0菜单的tool中的Selection Tool中来设置,可以使Intel C++ 编译器取代Microsoft Visual C++ 6.0开发环境中的编译器,作为默认编译器。而在Microsoft Visual C++.NET 2003中您可以直接通过右键的快捷菜单将工程转换成使用Intel C++系统的工程。你还可以定义宏_USE_INTEL_COMPILER 和_USE_NON_INTEL_COMPILER 作为特定工程的编译器。
Intel C++ 编译器同样支持Linux 平台,而且具有与Windows版的相同的特性。你可以在Intel开发工具的网站上找到更详细的关于Windows和Linux版的Intel C++编译器和Intel Fortran编译器的信息。

2 针对于特定处理器的优化
我们总希望我们开发的程序可以利用处理器的所有特性,使程序的运行效率达最好。编译器是否支持新型处理器的新指令和代码调度规则决定着生成程序能否充分利用处理器的所有特性。Intel C++编译器支持新型处理器的新指令和代码调度规则。在使用特定处理器指令时,例如只能在Pentium4处理器和其后续的处理器上使用的流式单指令流多数据流扩展(Streaming SIMD Extensive),编译器可以同时生成可以在老式处理器上执行的代码。这样编译器输出的程序可以在新型的处理器上获得最佳的性能,同时也可运行于所有老式的处理器上。
针对特定处理器上更有效,同时生成的代码可以运行在所有的处理器上:

选项

处理器
/G1Itanium processor
/G2Itanium2 processor
/G5Pentium processor, Pentium processor with MMX technology
/G6Pentium Pro, Pentium II, and Pentium III processors
/G7Pentium 4 processor
生成只能在特定或更新的处理器上运行的代码:

选项

处理器
/QxiPentium Pro and Pentium II processors(CMOV, FCMOV)
/QxMPentium processor with MMX technology
/QxKPentium III processor with Streaming SIMD Extensions
/QxWPentium 4 processor with Streaming SIM
/QxNIntel Pentium 4 and compatible Intel processors。当程序的main()函数使用这个选项编译时,编译器将生成检测不兼容处理器的代码,并在执行时生成一个错误的消息。而且,除了针对Intel处理器的优化,这个选项同时采用了新的优化策略。
/QxBIntel Pentium M and compatible Intel processors。当程序的main()函数使用这个选项编译时,编译器将生成检测不兼容处理器的代码,并在执行时生成一个错误的消息。而且,除了针对Intel处理器的优化,这个选项同时采用了新的优化策略。
/QxPIntel Pentium 4 processor with Streaming SIMD Extensions 3(SSE 3)。当程序的main()函数使用这个选项编译时,编译器将生成检测不兼容处理器的代码,并在执行时生成一个错误的消息。而且,除了针对Intel处理器的优化,这个选项同时采用了新的优化策略。
生成多种执行代码,一种在特定处理器上执行,而另一种通用代码可以在其他的所有的处理器上执行:

选项

处理器
/QaxiPentium Pro and Pentium II processors(CMOV, FCMOV)
/QaxMPentium processor with MMX technology
/QaxKPentium III processor and compatible Intel processors.
/QaxWPentium 4 processor and compatible Intel processors.
/QaxNIntel Pentium 4 and compatible Intel processors。除了针对Intel处理器的优化,这个选项同时采用了新的优化策略。
/QaxBIntel Pentium M and compatible Intel processors。除了针对Intel处理器的优化,这个选项同时采用了新的优化策略。
/QaxPIntel Pentium 4 processor with Streaming SIMD Extensions 3(SSE 3)。除了针对Intel处理器的优化,这个选项同时采用了新的优化策略。
以上编译器的选项可以合并。例如,要使用SSE指令同时希望在Pentium 4处理器上运行,你可以同时使用 G7 QxK 选项。在Microsoft Visual C++ 6.0 中,这些选项可以从Microsoft Visual C++ 的Project Settings 对话框中添加进工程。而在Microsoft Visual C++.NET 2003中您可以直接通过在Intel Specific选择使用这些选项。

3 编写针对适用于特定处理器的函数
为使程序利用特定处理器的特性,有时你不得不用一些特定指令来写一些函数,例如MMX指令,只有特定的处理器支持这些指令。这时编译器需要一些CPU的监测代码。通过调用汇编指令CPUID可以确定CPU的型号。调用该指令时将EAX 寄存器置1(可以参考 Intel Architecture Software Developer's Manual, Volume 2: Instruction Set Reference and Application Note AP-485 Intel Processor Identification and the CPUID Instruction)指令执行完后,处理器的信息和其他一些信息如CPU 特性信息和Cache 大小的信息会被置入相应的寄存器。你可以在程序中使用这些信息,在不同的处理器上选择调用不同的函数。
一种相对简单但可以达到同样目的的方法是使用Intel C++编译器的dispatch特性。编译器会自动生成高效的CPU检测代码。使定义一个在特定处理器上执行的函数变得更加简单,而不必处理琐碎的关于CPU ID 指令细节的问题。如下面的例子所示,在函数声明中使用关键字cpu_dispatch和cpu_specific,编译器在特定处理器上调用特定函数。
__declspec ( cpu_specific (generic) ) void fn(void)
{
// 将针对i386 的通用代码放置于此
}
__declspec ( cpu_specific (Pentium 4) ) void fn(void)
{
// 添加针对奔腾4 处理器的代码于此
}
__declspec ( cpu_dispatch (generic, Pentium_4) ) void
fn(void)
{
// 将函数体置空,不要放置任何代码
// 编译器将会依据CPU类型添加相应代码
}

4使用SIMD 指令
在程序中使用SIMD 指令可以使程序的性能得到大幅度提高,但是C/C++语言本身中并没有提供直接使用它们的方法。过去,只有通过手写汇编语言的方式使用SIMD 指令,但是这意味着额外的开发,调试和维护工作。幸运的是Intel C++编译器在C/C++ 语言中增加了对SIMD指令的支持,这使使用SIMD变得更加简便。有四种方法可以在Intel/C++编译器中使用SIMD 指令:

4.1Automatic Vectorzation
使用这种方式,Intel C++编译器可以分析程序中的循环,并自动使用SIMD指令,命令行选项 Q[a]x{I|M|K|W}会通知编译器使用SIMD 指令是安全的。下面的例子展示了使SIMD 指令是安全的。下面的例子展示了使用QxW选项许可编译器使用Pentium 4 处理器特有的指令。

C:/dev/simd> icl "Cc "CQxW Simd.cpp
Intel(R) C++ Compiler for 32-bit applications, Version
8.0 Build 20040318Z
Package ID: W_CC_PC_8.0.048
Copyright (C) 1985-2004 Intel Corporation. All rights
reserved.
Simd.cpp
Simd.cpp(8) : (col. 2) remark: LOOP WAS VECTORIZED.
Simd.cpp(21) : (col. 2) remark: LOOP WAS VECTORIZED.

有非常多的命令行参数和progmas control automatic vectoriazation 你可以参考 Intel C++ Compiler User's Guide以获得更多的信息。在Intel Software Development Products Web Site可以得到更多的信息。

4.2 支持SIMD 的C++类库
令人兴奋的是,Intel C++ 编译器包含直接使用SIMD指令的数据类型你可以使用这些数据类型以获得对生成代码的控制。使用这些数据类型,仅需要声明一个需要数据类型的变量,这样可以增加一次处理的元素数目,从而减少循环的次数。
下面例子展示了一个使用了I32vec4数据类型的转换(捆绑四个32-bit的整数):

//Original version using integers
Void quarter (int array[], int len)
{
int i;
for (i=0:i array[i] = array [i] >>2;
}

// Modified version using Isvec4, 4 SIMD integers
Void quarterVec(int array[], int len)
{
// assumes len is a multiple of 4
// assumes array is 16 byte aligned
Is32vec4 *array4 = (Is32vec4 *)array
int i;

for (i=0; i array4[i]= array4[i]>>2;
}

4.3Intrinsics
Intel C++编译器支持使用本生(Intrinsics)函数,它支持是映射到SIMD 指令和其它非常多的汇编指令。以下示例展示了与前面相同的quarter()函数使用本生函数,可以看出得到的汇编程序是相同的,只是它使用C/C++ 变量替代寄存器。关于本生函数的文档可以在IA-32 Intel Architecture Software Developer's Manual, Volume 2: Instruction Set Reference 和Intel C++ Compiler User's Guide上找到。

4.4 内嵌汇编语言
有时我们不得不在最底层编写程序。内嵌汇编语言使在最底层编码成为可能。以下示例展示了与前面相同的函数使用内嵌汇编:

void quarterAsm(int array[], int len)
{
_asm{
mov esi, array; esi = array pointer
mov ecx, len; ecx = loop counter
shr ecx, 2; 4 shifts per loop iteration

theloop:
movdqa xmm0, [esi]; load 4 integers
psrad xmm0, 2; shift right all 4 integers
movdqa [esi] , xmm0; aligned store
add esi, 16; move array pointer
sub ecx, 1; decrement loop counter
jnz theloop
}
}

4.5 其他编译器优化
除了automatic vectorization和使用SIMD指令,使用Intel C++ 编译器可以做很多其它的优化,例如表列出的:

优化特性

开关
描述
循环展开-Qunroll
将循环按照指定的最大次数自动展开
使用舍入法进行浮点到整型的转换-Qrcd改变浮点到整型转换的方式。当使用该选项时,编译器将使用舍入法而不是通常的截断方式进行转换。这样做可以提高程序性能,但同时也会带来一些兼容性方面的问题。该选项在进行性能调试时很值得进行尝试。
性能制导优化-Qprof_gen
-Qprof_use
一种包含 Instrumented 编译,instrumented 运行和反馈式编译的三步式优化过程。
过程间优化-Qip
-Qipo
-wp ipo
一种两段式的编译时优化方法。该方法在多个文件或整个程序,而不只是在单个函数中寻找更佳的优化。
关于如何使用这些和其它的优化的详细的信息请查阅Intel C++ Compiler User's Guide and Reference。

小结
本文介绍了如何使用Intel C++编译器的优化程序。为达到最优的性能,你需要始终打开合适的编译器的优化项。这些选项有助于在早期发现优化程序时的问题,而且这时更容易发现和修补问题,并且有助你关注程序性能。只有在调试需要时,你才应该关掉这些编译器优化选项。
由于Intel C++编译器的优化选项众多,使用之前简略地读一下编译器的文档是很有必要的。这会有助于清楚所有可能的优化选项和性能特性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: