在XeonPhi上使用AVX内联汇编的矢量和
我是新手使用XeonPhi Intel协处理器。 我想使用AVX 512位指令为简单的矢量和编写代码。 我使用k1om-mpss-linux-gcc作为编译器,并希望编写内联汇编。 这是我的代码:
#include #include #include #include #include #include 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,mnemoics vmovdqa32和vpaddd是相同的。
操作码可能有所不同,但编译器/汇编器负责这一点。 在OPs的情况下,他/她正在使用特殊版本的GCC, k1om-mpss-linux-gcc
,它是许多核心软件堆栈 KNC的一部分,可能会生成正确的操作码。 可以使用k1om-mpss-linux-gcc
在主机上k1om-mpss-linux-gcc
,然后将二进制文件scp
到KNC卡。 我从这个问题的评论中了解到了这一点 。
至于为什么OPs代码失败,我只能猜测,因为我没有KNC卡进行测试。
在我对GCC内联汇编的有限经验中,我了解到在目标文件中查看生成的程序集以确保编译器完成您期望的操作是很好的。
当我用正常版本的GCC编译你的代码时,我看到行"vpaddd %0,%%zmm0,%%zmm1;"
用分号生成组件。 我不认为分号应该在那里。 这可能是一个问题。
但由于OPs助记符与AVX512相同,我们可以使用AVX512内在函数来确定正确的汇编
#include 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" );
也许应该使用vmovdqa64
而不是vmovdqa64
但我希望它没关系。
我使用了寄存器修饰符r
而不是内存修饰符m
因为从过去的经验来看,内存修饰符并没有产生我期望的汇编。
另一种考虑的可能性是使用支持AVX512内在函数的GCC版本来生成程序集,然后使用特殊的KNC版本的GCC将程序集转换为二进制。 例如
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
的目的是用于屏蔽,这可以用这些东西进行
_mm512_mask_load/store_epi32 _mm512_mask_load/store_epi64
没有面具,说明是等效的。
Xeon Phi Knights Corner不支持AVX。 它仅支持一组特殊的向量扩展,称为Intel初始多核指令( Intel IMCI ),矢量大小为512b。 因此,尝试将任何类型的AVX特定程序集放入KNC代码中将导致崩溃。
等待骑士降落。 它将支持AVX-512矢量扩展。