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

时间:2015-12-06 04:36:12

标签: c linux inline-assembly xeon-phi avx512

我是新手使用XeonPhi Intel协处理器。我想使用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部分得到了分段错误。有人可以帮我吗???

谢谢

2 个答案:

答案 0 :(得分:4)

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

等待骑士降落。它将支持AVX-512矢量扩展。

答案 1 :(得分:2)

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

操作码可能有所不同,但编译器/汇编器负责这一点。在OPs的情况下,他/她正在使用特殊版本的GCC,k1om-mpss-linux-gcc,它是many core software stack KNC的一部分,可能会生成正确的操作码。可以使用k1om-mpss-linux-gcc在主机上编译,然后使用scp二进制文件在KNC卡上编译。我从this question的评论中了解到了这一点。

至于OPs代码失败的原因我只能猜测,因为我没有KNC卡可供测试。

在我对GCC内联汇编的有限经验中,我了解到在目标文件中查看生成的程序集以确保编译器完成您期望的操作是很好的。

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

但由于OPs助记符与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 procudes

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

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

movapdvmovdqa32的目的是掩盖可以对这些内部进行掩饰

vmovdqa64

没有面具,说明是等效的。