这是我第一次向 Stackoverflow 社区提问。很抱歉,如果我的问题不符合论坛的风格/大小 - 会随着经验而改进。
我正在尝试使用 Intel Compiler 14.0.1 对 C++ 中的循环进行矢量化,以更好地利用宽 512 位寄存器在 Intel Xeon Phi 上进行速度优化。(受https://software.intel.com/en-us/articles/data-alignment-to-assist-vectorization启发)和 Google 上的大量参考资料表明,数据对齐在 Xeon Phi 上比在现代 Xeon 处理器上更重要,它仍然很重要(其中一个在很好的概述中https://indico.cern.ch/event/238763/material/slides/6.pdf第 18 页)。
这个问题有点类似于unaligned memory accesses,但涵盖了一个更简单/更广泛的示例,并希望有一个更明确的答案。
一段代码示例:
#include <malloc.h>
void func(float *const y, float *const x, const int & N, const float & a0, const float & a1, const float & a2, const float & a3)
{
__assume(N%16 == 0); // aim is to let compiler know that there is no residual loop (not sure if it works as expected, though)
int i;
#pragma simd // to assume no vector dependencies
#pragma loop count min=16, avg=80, max=2048 // to let compiler know for which cases to optimize (not sure if it is beneficial)
//#pragma vector aligned // to let compiler know that all the arrays are aligned... but not in this case
for (i = 0; i < N; i++)
{
y[i] = fmax(x[i + 1] * a0 + x[i] * a1, x[i] * a2 + a3);
}
}
int main{
...
//y and x are _mm_malloced with 64 byte alignment, e.g.
float * y = (float *)_aligned_malloc(int_sizeBytes_x_or_y + 64, 64); //+64 for padding to enable vectorisation without using mask on the residual loop
float * x = (float *)_aligned_malloc(int_sizeBytes_x_or_y + 64, 64);
...
//M = 160 to 2048, more often 160 (a multiple of 16 - floats per register)
for (int k = 0; k < M; k++)
{
...
//int N = ceil(k / 16.0) * 16; // to have no residual loop, not sure if beneficial
...
func(y, x, N, a0, a1, a2, a3);
...
}
...
_aligned_free(x);
_aligned_free(y);
}
func() 在主体中被调用 150-2000 次,为 x 和 y 重新使用预先分配的空间(为了避免持续的内存分配,这可能在 Phi 上比在普通 Xeon 上更耗时)。身体在每个核心上重复数百万次。
问题是 x[i] 和 x[i+1] 对于 512 位向量引擎来说本质上是未对齐的,由于 x[i+1] 部分的内存访问未对齐,因此向量化不是最佳的。
在 k++ 循环之前预先分配一个 64 字节对齐的 _x 一次,在 k++ 循环的每次迭代中执行 memcpy 以用 x 的前向值填充预分配的内存,在速度方面会有什么好处吗?(等价于
for (int j=0; j<N; j++) _x[0]=x[i+1]; with memcpy
),以便 #pragma 向量对齐可以在 func() 中使用y[i] = fmax(_x[i] * a0 + x[i] * a1, x[i] * a2 + a3);
?
是否有一些很好的方法可以有效地处理这个相当普遍的标准问题,以充分利用矢量引擎?
任何关于如何优化宽寄存器处理器矢量化的建议也非常受欢迎(这似乎是一个非常有趣的话题,英特尔最近的趋势是增强数据和任务并行性)