0

我编写了一个代码,在 Xeon Phi 英特尔协处理器上使用带有(512 位长向量)的 KNC 指令添加两个数组。但是,我在内联汇编部分中有分段部分。

这是我的代码:

int main(int argc, char* argv[])
{
    int i;
    const int length = 65536;
    const int AVXLength = length / 16;
    float *A = (float*) aligned_malloc(length * sizeof(float), 64);
    float *B = (float*) aligned_malloc(length * sizeof(float), 64);
    float *C = (float*) aligned_malloc(length * sizeof(float), 64);
    for(i=0; i<length; i++){
            A[i] = 1;
            B[i] = 2;
    }

    float * pA = A;
    float * pB = B;
    float * pC = C;
    for(i=0; i<AVXLength; i++ ){
         __asm__("vmovaps %1,%%zmm0\n"
                    "vmovaps %2,%%zmm1\n"
                    "vaddps %%zmm0,%%zmm0,%%zmm1\n"
                    "vmovaps %%zmm0,%0;"
            : "=m" (pC) : "m" (pA), "m" (pB));

            pA += 512;
            pB += 512;
            pC += 512;
    }
    return 0;
}

我使用 gcc 作为编译器(因为我没有钱购买 intel 编译器)。这是我编译这段代码的命令行:

k1om-mpss-linux-gcc add.c -o add.out

问题出在内联汇编中。以下内联汇编修复了它。

__asm__("vmovaps %1,%%zmm1\n"
        "vmovaps %2,%%zmm2\n"
        "vaddps %%zmm1,%%zmm2,%%zmm3\n"
        "vmovaps %%zmm3,%0;"
        : "=m" (*pC) : "m" (*pA), "m" (*pB));
4

1 回答 1

4

如前所述,Knights Corner (KNC)没有AVX512。但是,它确实有类似的东西。 事实证明,KNC 与 AVX512 的问题在这里是一个红鲱鱼。问题出在 OP 内联汇编中。

我建议您不要使用内联汇编,而是使用内在函数。KNC 内在函数在Intel Intrinsic Guide online中有描述。

此外, CERN 的 Przemysław Karpiński 扩展了 Agner Fog 的矢量类库以使用 KNC您可以在此处找到 git 存储库。如果您查看文件vectorf512_mic.h,您可以了解很多关于 KNC 内在函数的信息。

我将您的代码转换为使用这些内在函数(在这种情况下与 AVX512 内在函数相同):

int main(int argc, char* argv[])
{
    int i;
    const int length = 65536;
    const int AVXLength = length /16;
    float *A = (float*) aligned_malloc(length * sizeof(float), 64);
    float *B = (float*) aligned_malloc(length * sizeof(float), 64);
    float *C = (float*) aligned_malloc(length * sizeof(float), 64);
    for(i=0; i<length; i++){
        A[i] = 1;
        B[i] = 2;
    }
    for(i=0; i<AVXLength; i++ ){
        __m512 a16 = _mm512_load_ps(&A[16*i]);
        __m512 b16 = _mm512_load_ps(&B[16*i]);
        __m512 s16 = _mm512_add_ps(a16,b16);
        _mm512_store_ps(&C[16*i], s16);
    }
    return 0;
}

KNC 内部函数仅受 ICC 支持。然而,KNC 带有Manycore Platform Software Stack (MCSS),它带有一个特殊版本的 gcc,k1om-mpss-linux-gcc它可以通过内联汇编使用类似于 KNC 的 AVX512 功能。


在这种情况下,KNC 和 AVX512 的助记符相同。因此我们可以使用 AVX512 内部函数来发现要使用的程序集

void foo(int *A, int *B, int *C) {
    __m512i a16 = _mm512_load_epi32(A);
    __m512i b16 = _mm512_load_epi32(B);
    __m512i s16 = _mm512_add_epi32(a16,b16);
    _mm512_store_epi32(C, s16);
}

gcc -O3 -mavx512 knc.c生产

vmovaps (%rdi), %zmm0
vaddps  (%rsi), %zmm0, %zmm0
vmovaps %zmm0, (%rdx)

从这个使用内联汇编的解决方案将是

__asm__("vmovaps   (%1), %%zmm0\n"
        "vpaddps   (%2), %%zmm0, %%zmm0\n"
        "vmovaps   %%zmm0, (%0)"
        :
        : "r" (pC), "r" (pA), "r" (pB)
        :
);

使用前面的代码,GCC 会为每个数组生成添加指令。这是使用仅生成一个加法的索引寄存器的更好解决方案。

for(i=0; i<length; i+=16){
    __asm__ __volatile__ (
            "vmovaps   (%1,%3,4), %%zmm0\n"
            "vpaddps   (%2,%3,4), %%zmm0, %%zmm0\n"
            "vmovaps   %%zmm0, (%0,%3,4)"
            :
            : "r" (C), "r" (A), "r" (B), "r" (i)
            : "memory"
     );
 }

MPSS (3.6) 的最新版本包括支持 AVX512 内在函数的 GCC 5.1.1。因此,我认为只要 AVX512 内部函数与 KNC 内部函数相同,就可以使用它们,并且仅在它们不同意时才使用内联汇编。查看 Intel Intrinsic 指南表明它们在大多数情况下是相同的。

于 2015-12-11T10:27:46.907 回答