我认为您的三重循环不会自动矢量化。IMO的问题是:
- 通过对象类型 std::vector 访问内存。AFAIK 我认为任何编译器都不会自动矢量化 std::vector 代码,除非访问运算符 [] 或 () 被内联,但我仍然不清楚它是否会被自动矢量化。
- 您的代码受到内存别名的影响,即编译器不知道您引用的内存是否
img
从另一个内存指针访问,这很可能会阻止矢量化。基本上,您需要定义一个普通的双精度数组并提示编译器没有其他指针指向同一位置。我认为您可以使用__restrict
. __restrict
告诉编译器这个指针是唯一指向那个内存位置的指针,并且没有其他指针,因此没有副作用的风险。
默认情况下内存未对齐,即使编译器设法自动矢量化,未对齐内存的矢量化也比对齐内存的矢量化慢很多。您需要确保您的内存是 32 位地址对齐以利用自动矢量化和 AVX 到最大值和 16 位地址对齐以利用 SSE 到最大值,即始终与 32 位内存位地址对齐。您可以通过以下方式动态执行此操作:
double* buffer = NULL;
posix_memalign((void**) &buffer, 32, size*sizeof(double));
...
free(buffer);
在 MSVC 中,您可以这样做,__declspec(align(32)) double array[size]
但您必须检查您正在使用的特定编译器,以确保您使用的是正确的对齐指令。
另一个重要的事情是,如果您使用 GNU 编译器,请使用该标志-ftree-vectorizer-verbose=6
来检查您的循环是否正在自动矢量化。如果您使用 Intel 编译器,请使用-vec-report5
. 请注意,有多个级别的详细信息和信息输出,即 6 和 5 数字,因此请查看编译器文档。详细程度越高,代码中每个循环的向量化信息就越多,但编译器在发布模式下编译的速度越慢。
总的来说,我一直很惊讶让编译器自动矢量化是多么不容易,这是一个常见的错误,假设因为循环看起来很规范,那么编译器会神奇地自动矢量化它。
更新:还有一件事,确保您img
实际上是页面对齐的posix_memalign((void**) &buffer, sysconf(_SC_PAGESIZE), size*sizeof(double));
(这意味着 AVX 和 SSE 对齐)。问题是如果你有一个大图像,这个循环很可能会在执行过程中结束页面切换,这也非常昂贵。我认为这就是所谓的TLB未命中。