矢量化:什么时候值得手动展开循环?
Vectorization: when is worth manually unrolling loops?
我想大致了解何时可以期望编译器对循环进行向量化,以及何时值得我展开循环 帮助它决定使用矢量化。
我知道细节非常重要(什么编译器、什么编译选项、什么架构、我如何在循环中编写代码等),但我想知道是否有一些现代编译器的通用指南。
我会更具体地给出一个简单循环的例子(代码不应该计算任何有用的东西):
double *A,*B; // two arrays
int delay = something
[...]
double numer = 0, denomB = 0, denomA = 0;
for (int idxA = 0; idxA < Asize; idxA++)
{
int idxB = idxA + (Bsize-Asize)/2 + delay;
numer += A[idxA] * B[idxB];
denomA += A[idxA] * A[idxA];
denomB += B[idxB] * B[idxB];
}
我能否期望编译器对循环进行矢量化,或者像下面这样重写代码是否有用?
for ( int idxA = 0; idxA < Asize; idxA+=4 )
{
int idxB = idxA + (Bsize-Asize)/2 + delay;
numer += A[idxA] * B[idxB];
denomA += A[idxA] * A[idxA];
denomB += B[idxB] * B[idxB];
numer += A[idxA+1] * B[idxB+1];
denomA += A[idxA+1] * A[idxA+1];
denomB += B[idxB+1] * B[idxB+1];
numer += A[idxA+2] * B[idxB+2];
denomA += A[idxA+2] * A[idxA+2];
denomB += B[idxB+2] * B[idxB+2];
numer += A[idxA+3] * B[idxB+3];
denomA += A[idxA+3] * A[idxA+3];
denomB += B[idxB+3] * B[idxB+3];
}
简答,正如其他人所说:如果您不指定编译器或目标体系结构,则没有通用指南。
顺便说一句,现在让编译器优化代码通常更好,因为它 "knows" 更好地提高了架构的可能性。在某些情况下展开循环不会更快。
如果有人看到并需要它,GCC 中有 -funroll-loops
标志。
我从其他答案和评论中了解到,不建议手动展开循环:编译器知道得更多。
但是 compiler might fail to vectorize your code depending on the optimization options used in compilation. Why? Because floating points addition and multiplication are neither associative nor commutative. This prevents the compiler from reordering operands,这反过来会在您希望对代码进行矢量化的某些情况下阻止矢量化。
我想大致了解何时可以期望编译器对循环进行向量化,以及何时值得我展开循环 帮助它决定使用矢量化。
我知道细节非常重要(什么编译器、什么编译选项、什么架构、我如何在循环中编写代码等),但我想知道是否有一些现代编译器的通用指南。
我会更具体地给出一个简单循环的例子(代码不应该计算任何有用的东西):
double *A,*B; // two arrays
int delay = something
[...]
double numer = 0, denomB = 0, denomA = 0;
for (int idxA = 0; idxA < Asize; idxA++)
{
int idxB = idxA + (Bsize-Asize)/2 + delay;
numer += A[idxA] * B[idxB];
denomA += A[idxA] * A[idxA];
denomB += B[idxB] * B[idxB];
}
我能否期望编译器对循环进行矢量化,或者像下面这样重写代码是否有用?
for ( int idxA = 0; idxA < Asize; idxA+=4 )
{
int idxB = idxA + (Bsize-Asize)/2 + delay;
numer += A[idxA] * B[idxB];
denomA += A[idxA] * A[idxA];
denomB += B[idxB] * B[idxB];
numer += A[idxA+1] * B[idxB+1];
denomA += A[idxA+1] * A[idxA+1];
denomB += B[idxB+1] * B[idxB+1];
numer += A[idxA+2] * B[idxB+2];
denomA += A[idxA+2] * A[idxA+2];
denomB += B[idxB+2] * B[idxB+2];
numer += A[idxA+3] * B[idxB+3];
denomA += A[idxA+3] * A[idxA+3];
denomB += B[idxB+3] * B[idxB+3];
}
简答,正如其他人所说:如果您不指定编译器或目标体系结构,则没有通用指南。
顺便说一句,现在让编译器优化代码通常更好,因为它 "knows" 更好地提高了架构的可能性。在某些情况下展开循环不会更快。
如果有人看到并需要它,GCC 中有 -funroll-loops
标志。
我从其他答案和评论中了解到,不建议手动展开循环:编译器知道得更多。
但是 compiler might fail to vectorize your code depending on the optimization options used in compilation. Why? Because floating points addition and multiplication are neither associative nor commutative. This prevents the compiler from reordering operands,这反过来会在您希望对代码进行矢量化的某些情况下阻止矢量化。