向量化的方法

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

使用英特尔编译器进行自动向量化

作者:Yang Wang (Intel)

自动向量化是英特尔编译器提供的一个可以自动的使用SIMD指示的功能。在处理数据时,编译器自动选择MMX™, Intel® Streaming SIMD 扩展(Intel® SSE, SSE2, SSE3 和SSE4)等指令集,对数据进行并行的处理。使用编译器提供的自动向量化功能是提高程序性能的一个非常有效的手段。自动向量化在IA-32和Intel® 64的平台上均提供很好的支持。

英特尔编译器提供的自动向量化相关的编译选项如下所示。”/Q”开头的选项是针对Windows平台的,“-“开头的选项是针对Linux*和Mac平台的。

-x, /Qx

按照该选项指定的处理器类型生成相应的优化代码。比如-xSSE3, 该选项指定编译器生成Intel® SSE3指令的代码。又比如-xSSE3_ATOM, 该选项针对Intel® Atom™ 处理器进行优化。

-ax, /Qax

如果指定该选项,在生成的单一目标文件中,不但会生成专门针对指定的处理器类型进行优化的代码,同时也生成通用的IA-32架构的代码。该选项主要是为了生成代码的兼容性考虑。

-vec, /Qvec

打开或者关闭编译器的向量化优化。默认情况下自动向量化是打开的。

-vec-report, /Qvec-report

该选项用户控制在编译过程中产生的向量化消息报告。

编译器提供的自动向量化优化默认情况下是打开的。在编译过程中我们可以使用-vec-report选项来打开向量化诊断消息报告。这样编译器可以告诉我们有哪些循环被向量化了,有哪些循环没有被向量化已经无法向量化的原因。

在编译程序的过程中,有时候我们会发现编译器报告说某个循环无法被向量化。很多时候无法向量化的原因都是因为循环中存在的变量依赖关系。有时候我们可以修改程序来消除这种依赖关系,有的时候我们可以使用编译器提供的一些编译指示来显示的告诉编译器如何处理这种依赖关系。即使在某个循环已经可以被自动向量化的时候,使用编译器提供的对向量化的语言支持和编译指示还可以提高编译器向量化的效率,提高程序执行的性能。

下面我们来详细解释一下编译器提供的编译指示以及这些指示对编译器编译的影响。

在Intel编译器中,我们提供下面这样一些对自动向量化的语言支持和编译指示。

__declspec(align(n))

指导编译器将变量按照n字节对齐

__declspec(align(n,off))

指导编译器将变量按照n字节再加上off字节的编译量进行对齐

restrict

消除别名分析中的二义性

__assume_aligned(a,n)

当编译器无法获取对齐信息时,假定数组a已经按照n字节对齐

#pragma ivdep

提示编译器忽略可能存在的向量依赖关系

#pragma vector {aligned|unaligned|always}

指定向量化的方式

#pragma novector

指定不做向量化

下面我们就这些编译指示做一些详细的了解。

__declspec(align(n))

首先我们来看看如何使用__declspec(align(n))来进行自动向量化。该指示主要是用来告诉编译器一个变量的对齐方式。当一个变量的对齐方式为16字节对齐时,生成的向量化指令是最高效的。当编译器不知道一个变量的对齐方式的时候,他可能没有办法对该变量的使用进行向量化,或者编译器会不得不生成条件语句来进行有条件的向量化。我们来看一下下面这个例子。

File: vec1.c

char a[];

void vec1(void)

{

int i;

for(i=0;i<1024;i++)

a[i] = 1;

}

使用下面命令编译该程序:

icl vec1.c -O2 -c /Qvec-report:3

这时编译器输出:

vec1.c

vec1.c(10) (col. 2): remark: LOOP WAS VECTORIZED.

我们可以看到编译器对上面这个程序进行了自动向量化。但是实际上,由于编译器不知道a的对齐字节数,所以他对变量a数据进行了一些条件处理。我们可以在编译的时候加上-S选项来查看编译器生成的汇编代码。实际上,编译器对该程序做了如下处理:

temp = a&0x0f;

if(temp != 0)

{

temp = 16-temp;

for(i=0;i

}

/*下面是对齐的访问*/

for(i=temp;i<1024;i++) a[i] = 1;

如果我们指定了a的对齐方式,比如我们使用__declspec(align(16))来定义变量a, 如下所示:

__declspec(align(16)) char a[];

void vec1(void)

{

int i;

for(i=0;i<1024;i++)

a[i] = 1;

}

对于上面这段程序,编译器做了自动向量化。如果我们检查生成的汇编代码可以发现,对于a的对齐方式的条件处理代码已经不见了。

正确使用__declspec(align(n))以及其他align相关编译指示可以帮助编译器进行自动向量化以及提高编译器自动向量化的效率。

restrict

使用restrict关键字可以显式的告诉编译器取消指针之间的二义性。我们还是用下面这个例子来说明restrict的使用。

File: vec2.c

void vec2(char* a, char* b, int n)

{

int i;

for(i=0;i

a[i] = b[i];

}

使用以下命令进行编译:

icl vec2.c -O2 -c /Qvec-report:3

编译器输出:

vec2.c

restrict.c(5) (col. 2): remark: LOOP WAS VECTORIZED.

restrict.c(5) (col. 2): remark: loop skipped: multiversioned.

此时编译器输出multiversioned向量化代码,对同一个循环产生了多个版本的实现。实际上编译器对代码进行了条件处理,如下所示:

void vec2(char* a, char* b, int n)

{

int i;

if(a+n

for(i=0;i

else

for(i=0;i

}

可以看出,由于编译器不知道指针a 和b之间是否存在重叠,所以他对循环进行了条件处理。如果我们从语义上能够保证a和b是不会发生重叠的,那么我们就可以使用restrict关键字来显式的告诉编译器这一点。

void vec2(char* restrict a, char* restrict b, int n)

{

int i;

for(i=0;i

a[i] = b[i];

}

此时编译器输出没有multiversioned。

restrict.c

restrict.c(5) (col. 2): remark: LOOP WAS VECTORIZED.