对代码进行矢量化是个好主意吗?何时执行此操作有哪些良好做法?下面会发生什么?
矢量化意味着编译器检测到您的独立指令可以作为一条 SIMD 指令执行。通常的例子是,如果你做了类似的事情
for (i = 0; i < N; i++) {
a[i] = a[i] + b[i];
}
它将被矢量化为(使用矢量符号)
for (i = 0; i < (N - N % VF); i += VF) {
a[i : i + VF] = a[i : i + VF] + b[i : i + VF];
}
基本上,编译器会选择一个可以同时对数组的 VF 元素执行的操作,并执行该操作 N/VF 次,而不是执行单个操作 N 次。
它提高了性能,但对架构提出了更多要求。
如上所述,向量化用于利用SIMD指令,它可以对打包到大型寄存器中的不同数据执行相同的操作。
使编译器能够自动矢量化循环的通用准则是确保循环的不同迭代中不存在流依赖和反依赖黑白数据元素。
http://en.wikipedia.org/wiki/Data_dependency
一些编译器(例如 Intel C++/Fortran 编译器)能够自动向量化代码。如果无法对循环进行矢量化,英特尔编译器能够报告无法执行此操作的原因。这些报告可用于修改代码,使其变得可矢量化(假设可能)
《优化现代架构编译器:基于依赖的方法》一书深入介绍了依赖关系
矢量化不必局限于可以容纳大数据的单个寄存器。就像使用“128”位寄存器来保存“4 x 32”位数据一样。这取决于架构限制。某些架构具有不同的执行单元,这些执行单元具有自己的寄存器。在这种情况下,可以将部分数据馈送到该执行单元,并且可以从与该执行单元对应的寄存器中获取结果。
例如,考虑以下情况。
for(i=0; i < N; i++)
{
a[i] = a[i] + b[i];
}
如果我正在开发一个具有两个执行单元的架构,那么我的向量大小定义为 2。上面提到的循环将被重新构造为
for(i=0; i<(N/2); i+=2)
{
a[i] = a[i] + b[i] ;
a[i+1] = a[i+1] + b[i+1];
}注意:for 语句中的 2 是从向量大小导出的。
由于我有两个执行单元,循环内的两个语句将被输入到两个执行单元中。总和将分别累加到执行单元中。最后将进行累加值(来自两个执行单元)的总和。
好的做法是
1. 在向量化循环之前需要检查依赖关系(循环的不同迭代之间)等约束。
2. 需要防止函数调用。
3. 指针访问会产生别名,需要防止它。
这是SSE代码生成。
您有一个包含浮点矩阵代码的循环:matrix1[i][j] + matrix2[i][j],编译器会生成 SSE 代码。