首页 文章

使用宽对齐的未对齐数据访问进行矢量化/优化循环(特别是Xeon Phi)

提问于
浏览
4

这是我向Stackoverflow社区提问的第一次经历 . 对不起,如果我的问题不符合论坛的风格/大小 - 将随着经验而改善 .

我正在尝试使用英特尔编译器14.0.1在C中对一个循环进行矢量化,以便更好地利用宽512位寄存器来优化英特尔至强融核的速度 . (受https://software.intel.com/en-us/articles/data-alignment-to-assist-vectorization的启发)以及Google上的众多参考文献,数据对齐在Xeon Phi上比在现代Xeon处理器上更为重要,现代Xeon处理器仍然非常重要(其中一个在第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 vector aligned可以在func()里面用 y[i] = fmax(_x[i] * a0 + x[i] * a1, x[i] * a2 + a3);

是否有一些很好的方法可以有效地处理这个相当普遍的标准问题,以充分利用矢量引擎?

关于如何优化宽寄存器处理器的矢量化的任何建议也是非常受欢迎的(这似乎是一个非常有趣的主题,英特尔最近的趋势是增强数据以及任务并行性)

1 回答

  • 3

    即使在这种情况下,最好让编译器知道数组是对齐的 . 如:__assume_aligned(x,64)__assume_aligned(y,64)

    对于__assume(N%16 == 0),这有时会有所帮助,但您会看到它在具有内部和外部循环的代码中最常使用 . 当N%16不等于0时产生的残余循环的成本很小,如果你只碰到它一次 . 但是,在这种情况下,您将重复调用该函数 . 所以它可能有助于更大的M值 .

    分配第二个数组并使用从x [1]开始的值填充它不是一个好主意 . 与略微未对齐的内存访问相比,memcpy太昂贵了 .

    您可以尝试重写代码以使用_mm512_alignr_epi32内在函数 . 我试图找到一个很好的例子来指出你但尚未找到一个 . 但是在这种情况下使用_mm512_alignr_epi32可能不会让你受到太大影响,你只使用2个向量 .

相关问题