我是新来使用xeonphi英特尔协处理器。我想用avx 512位指令为一个简单的向量和编写代码。我使用k1ommpsslinuxgcc作为编译器,希望编写内联程序集。这是我的代码:

#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部分存在分段错误。有人能帮我吗???
谢谢

最佳答案

虽然骑士角(knights corner,knc)没有avx512,但它有一些非常相似的东西。许多记忆法是一样的。事实上,在op的情况下,avx512和knc的助记符vmovdqa32vpaddd是相同的。
操作码可能不同,但编译器/汇编程序会处理这个问题。在操作案例中,他/她使用的是一个特殊版本的gcc,k1om-mpss-linux-gcc它是many core software stackknc的一部分,knc可能会生成正确的操作码。可以使用k1om-mpss-linux-gcc在主机上编译,然后将二进制文件scp编译到knc卡。我从this question中的一条评论中了解到了这一点。
至于为什么操作代码失败,我只能猜测,因为我没有一个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程序
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,但我想这无关紧要。
我使用了register修饰符r而不是memory修饰符m,因为根据以往的经验,memory修饰符没有生成我所期望的程序集。
另一种可能的考虑是使用支持avx512内部函数的gcc版本生成程序集,然后使用gcc的特殊knc版本将程序集转换为二进制。例如
gcc-5.1 -O3 -S foo.c
k1om-mpss-linux-gcc foo.s

这可能是自找麻烦,因为m可能是gcc的旧版本。我以前从来没有做过这样的事,但可能行得通。
正如所解释的,avx512内部函数的原因
_mm512_load/store(u)_epi32
_mm512_load/store(u)_epi64
_mm512_load/store(u)_si512

参数已转换为k1om-mpss-linux-gcc。例如,在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

我还不清楚为什么有“CC”和“cc>”(GCC现在似乎只使用void*),但在SSE中可能类似于vmovdqa32vmovdqa64,它们没有真正的区别,只存在于将来可能有所不同的情况下。
vmovdqa64movaps的目的是掩盖这些信息
_mm512_mask_load/store_epi32
_mm512_mask_load/store_epi64

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

关于c - 在XeonPhi上使用AVX内联汇编的 vector 和,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34114092/

10-16 10:27