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

C 在XeonPhi上使用AVX内联汇编的向量和,c,linux,inline-assembly,xeon-phi,avx512,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&

我是新使用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]);
    }

}
#包括
#包括
#包括
#包括
#包括
#包括
void*对齐\u malloc(大小、大小对齐){
uintpttr_t r=(uintpttr_t)malloc(大小+--对齐+sizeof(uintpttr_t));
uintpttr_t=r+sizeof(uintpttr_t);
uintptr_t o=(t+对齐)和~(uintptr_t)对齐;
如果(!r)返回NULL;
((uintpttr_t*)o)[-1]=r;
返回(无效*)o;
}
int main(int argc,char*argv[])
{
printf(“开始计算…\n”);
int i;
常数int长度=65536;
无符号*A=(无符号*)对齐\u malloc(长度*sizeof(无符号),64);
无符号*B=(无符号*)对齐\u malloc(长度*sizeof(无符号),64);
无符号*C=(无符号*)对齐\u malloc(长度*sizeof(无符号),64);

对于(i=0;iXeon Phi Knights Corner不支持AVX。它只支持一组特殊的向量扩展,称为Intel Initial Many Core指令(),向量大小为512b。因此,尝试将任何特定于AVX的程序集放入KNC代码将导致崩溃


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

Xeon Phi Knights Corner不支持AVX。它只支持一组特殊的矢量扩展,称为“英特尔初始多核指令”(Intel Initial Many Core Instructions(),矢量大小为512b)。因此,尝试将任何特定于AVX的程序集放入KNC代码中都会导致崩溃

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

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

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


至于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选择了
vmovdqa64
而不是
vmovdqa32
,尽管英特尔文档说它应该是
vmovdqa32
。我不知道为什么。我不知道区别是什么。我本可以使用固有的
\u mm512\u load_si512
,它确实存在,并且根据英特尔应该映射
vmovdqa32
,但GCC是将它也映射到
vmovdqa64
。我不确定为什么现在也有
\u mm512\u load\u epi32
\u mm512\u load\u 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。我以前从未做过类似的事情,但它可能会工作


如前所述,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
我仍然不清楚为什么会有
vmovdqa32
vmovdqa64
(GCC目前似乎只使用
vmovdqa64
),但它可能类似于SSE中的
movaps
movapd
,它们没有真正的区别,只存在于它们可能在未来产生差异的情况下


vmovdqa32
vmovdqa64
的目的是为了屏蔽这些INTRSIC

_mm512_mask_load/store_epi32
_mm512_mask_load/store_epi64
没有掩码,指令是等效的。

虽然骑士角(KNC)没有AVX512,但它有一些非常相似的东西。许多助记符是相同的。事实上,在OP的例子中,AVX512和KNC的助记符和是相同的

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


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

在我有限的GCC内联汇编经验中,我了解到最好在对象文件中查看生成的汇编,以确保编译器达到预期效果

当我用普通的ve编译你的代码时
_mm512_mask_load/store_epi32
_mm512_mask_load/store_epi64