将代码矢量化是个好主意吗?在什么时候做这件事上有什么好的做法?下面会发生什么?
5 回答
矢量化意味着编译器检测到您的独立指令可以作为一条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 指令,该指令可以对打包到大寄存器中的不同数据执行相同的操作。
使编译器能够自动向量化循环的通用准则是确保在循环的不同迭代中没有流依赖和反依赖 b/w 数据元素。
http://en.wikipedia.org/wiki/Data_dependency
英特尔 C++/Fortran 编译器等一些编译器能够自动矢量化代码。如果它无法向量化循环,英特尔编译器能够报告它为什么不能这样做。有报告可用于修改代码,使其变得可矢量化(假设它是可能的)
在“为现代架构优化编译器:基于依赖的方法”一书中深入介绍了依赖关系
向量化不必局限于可以保存大数据的单个寄存器。就像使用“128”位寄存器来保存“4 x 32”位数据一样。这取决于架构限制。一些架构有不同的执行单元,它们有自己的寄存器。在这种情况下,可以将一部分数据馈送到该执行单元,并且可以从对应于该执行单元的寄存器中获取结果。
例如,考虑以下情况。
for(i=0; i < N; i++)
{
a[i] = a[i] + b[i];
}
如果我正在研究具有两个执行单元的架构,那么我的向量大小被定义为两个。上面提到的循环将被重新定义为
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 代码。