我是新来使用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的助记符vmovdqa32和vpaddd是相同的。
操作码可能不同,但编译器/汇编程序会处理这个问题。在操作案例中,他/她使用的是一个特殊版本的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中可能类似于vmovdqa32
和vmovdqa64
,它们没有真正的区别,只存在于将来可能有所不同的情况下。vmovdqa64
和movaps
的目的是掩盖这些信息_mm512_mask_load/store_epi32
_mm512_mask_load/store_epi64
没有掩码,指令是等价的。
关于c - 在XeonPhi上使用AVX内联汇编的 vector 和,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34114092/