在 XeonPhi 上使用 AVX 内联汇编的矢量和

Vector Sum using AVX Inline Assembly on XeonPhi

我是英特尔至强融核协处理器的新手。我想使用 AVX 512 位指令为简单的矢量和编写代码。我使用 k1om-mpss-linux-gcc 作为编译器并想编写内联汇编。这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <assert.h>
#include <stdint.h>

void* aligned_malloc(size_t size, size_t alignment) {

    uintptr_t r = (uintptr_t)malloc(size + --alignment + sizeof(uintptr_t));
    uintptr_t t = r + sizeof(uintptr_t);
    uintptr_t o =(t + alignment) & ~(uintptr_t)alignment;
    if (!r) return NULL;
    ((uintptr_t*)o)[-1] = r;
    return (void*)o;
}

int main(int argc, char* argv[])
{
    printf("Starting calculation...\n");
    int i;
    const int length = 65536;

    unsigned *A = (unsigned*) aligned_malloc(length * sizeof(unsigned), 64);
    unsigned *B = (unsigned*) aligned_malloc(length * sizeof(unsigned), 64);
    unsigned *C = (unsigned*) aligned_malloc(length * sizeof(unsigned), 64);

    for(i=0; i<length; i++){
            A[i] = 1;
            B[i] = 2;
    }

    const int AVXLength = length / 16;
    unsigned char * pA = (unsigned char *) A;
    unsigned char * pB = (unsigned char *) B;
    unsigned char * pC = (unsigned char *) C;
    for(i=0; i<AVXLength; i++ ){
            __asm__("vmovdqa32 %1,%%zmm0\n"
                    "vmovdqa32 %2,%%zmm1\n"
                    "vpaddd %0,%%zmm0,%%zmm1;"
            : "=m" (pC) : "m" (pA), "m" (pB));

            pA += 64;
            pB += 64;
            pC += 64;
    }

    // To prove that the program actually worked
    for (i=0; i <5 ; i++)
    {
            printf("C[%d] = %f\n", i, C[i]);
    }

}

然而,当我 运行 程序时,我从 asm 部分得到了分段错误。有人可以帮我吗???

谢谢

Xeon Phi Knights Corner 不支持 AVX。它只支持一组特殊的向量扩展,称为 Intel Initial Many Core Instructions (Intel IMCI),向量大小为 512b。因此,尝试将任何类型的 AVX 特定程序集放入 KNC 代码中都会导致崩溃。

静待骑士登陆。它将支持 AVX-512 矢量扩展。

虽然 Knights Corner (KNC) 没有 AVX512,但它有一些非常相似的东西。许多助记符是相同的。事实上,在 OP 的情况下,助记符 vmovdqa32 and vpaddd 对于 AVX512 和 KNC 是相同的。

操作码可能不同,但 compiler/assembler 会处理这个问题。在 OPs 案例中 he/she 使用特殊版本的 GCC,k1om-mpss-linux-gcc 它是 many core software stack KNC which presumably generates the correct opcodes. One can compile on the host using k1om-mpss-linux-gcc and then scp the binary to the KNC card. I learned about this from a comment in this question.

的一部分

至于为什么 OPs 代码失败,我只能猜测,因为我没有 KNC 卡来测试。

根据我对 GCC 内联汇编的有限经验,我了解到最好在目标文件中查看生成的程序集以确保编译器按照您的预期进行。

当我用普通版本的 GCC 编译您的代码时,我看到行 "vpaddd %0,%%zmm0,%%zmm1;" 生成带有分号的程序集。我不认为分号应该在那里。这可能是一个问题。

但由于 OP 助记符与 AVX512 相同,我们可以使用 AVX512 内在函数找出正确的程序集

#include <x86intrin.h>
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 -mavx512f -O3 -S knc.c产生

vmovdqa64   (%rsi), %zmm0
vpaddd      (%rdi), %zmm0, %zmm0
vmovdqa64   %zmm0, (%rdx)

GCC 选择了 vmovdqa64 而不是 vmovdqa32,尽管 Intel 文档说它应该是 vmovdqa32。我不确定为什么。我不知道有什么区别。我本可以使用确实存在的内在 _mm512_load_si512 并且根据 Intel 应该映射 vmovdqa32 但 GCC 也将其映射到 vmovdqa64 。我不确定为什么现在还有 _mm512_load_epi32_mm512_load_epi64。 SSE 和 AVX 没有这些对应的内在函数。

基于 GCC 的代码,这里是我将使用的内联程序集

__asm__ ("vmovdqa64   (%1), %%zmm0\n"
        "vpaddd      (%2), %%zmm0, %%zmm0\n"
        "vmovdqa64   %%zmm0, (%0)"
        :
        : "r" (pC), "r" (pA), "r" (pB)
        : "memory"
);

也许应该使用 vmovdqa32 而不是 vmovdqa64,但我认为这无关紧要。

我使用寄存器修饰符 r 而不是内存修饰符 m 因为根据过去的经验 m 内存修饰符没有产生我预期的程序集。


要考虑的另一种可能性是使用支持 AVX512 内部函数的 GCC 版本生成程序集,然后使用 GCC 的特殊 KNC 版本将程序集转换为二进制文件。例如

gcc-5.1 -O3 -S foo.c
k1om-mpss-linux-gcc foo.s

这可能是自找麻烦,因为 k1om-mpss-linux-gcc 可能是旧版本的 GCC。我以前从未做过这样的事情,但它可能会奏效。


正如解释的那样 here AVX512 内在函数的原因

_mm512_load/store(u)_epi32
_mm512_load/store(u)_epi64
_mm512_load/store(u)_si512

是参数转换为void*。例如,对于 SSE,您必须投射

int *x;
__m128i v;
__mm_store_si128((__m128*)x,v)

而有了 SSE,您不再需要

int *x;
__m512i;
__mm512_store_epi32(x,v);
//__mm512_store_si512(x,v); //this is also fine

我仍然不清楚为什么会有 vmovdqa32vmovdqa64(GCC 目前似乎只使用 vmovdqa64),但它可能类似于 movapsmovapd 在 SSE 中,它们没有真正的区别,只存在于它们可能在未来有所作为的情况下。


vmovdqa32vmovdqa64 的目的是为了屏蔽,可以用这些内部函数来做

_mm512_mask_load/store_epi32
_mm512_mask_load/store_epi64

没有掩码,指令是等效的。