“vmovaps”的分段错误

segmentation fault for `vmovaps'

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

这是我的代码:

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));

一样,Knights Corner (KNC) 没有 AVX512。但是,它确实有类似的东西。 事实证明,KNC vs AVX512 问题在这里是一个转移注意力的问题。问题出在 OPs 内联汇编中。

我建议您使用内部函数而不是使用内联汇编。 KNC 内在函数在 Intel Intrinsic Guide online.

中进行了描述

此外,Przemysław Karpiński at CERN extend Agner Fog's Vector Class Library to use KNC. You can find the git repository here. If you look in the file 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;
}

只有 ICC 支持 KNC 内部函数。但是,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 指南表明它们在大多数情况下是相同的。