2

我是使用 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 部分出现了分段错误。有人可以帮我吗???

谢谢

4

2 回答 2

4

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

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

于 2015-12-06T07:23:28.430 回答
2

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

操作码可能不同,但编译器/汇编器会处理这个问题。在 OP 的情况下,他/她使用的是特殊版本的 GCC,k1om-mpss-linux-gcc 它是许多核心软件堆栈KNC 的一部分,可能会生成正确的操作码。可以使用在主机上编译k1om-mpss-linux-gcc,然后scp将二进制文件写入 KNC 卡。我从这个问题的评论中了解到这一点。


至于为什么 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即使英特尔文档说它应该是vmovdqa32. 我不确定为什么。我不知道有什么区别。我本可以使用_mm512_load_si512确实存在的内在函数,并且根据英特尔应该映射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 版本来生成程序集,然后使用特殊的 KNC 版本的 GCC 将程序集转换为二进制。例如

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

我仍然不清楚为什么有vmovdqa32and vmovdqa64(GCC 似乎vmovdqa64目前只使用),但它可能类似于 SSE 中的movapsandmovapd并没有真正的区别,并且只存在于它们可能在未来产生影响的情况下。


vmovdqa32和的目的vmovdqa64是为了掩盖可以用这些内在函数做的事情

_mm512_mask_load/store_epi32
_mm512_mask_load/store_epi64

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

于 2015-12-12T16:58:39.860 回答