“vmovaps”的分段错误

我在Xeon Phi intel协处理器上编写了一个使用KNC指令(512位长vector)添加两个数组的代码。 不过,我在内联汇编部分有分割部分。

这里是我的代码:

int main(int argc, char* argv[]) { int i; const int length = 65536; const int AVXLength = length / 16; float *A = (float*) aligned_malloc(length * sizeof(float), 64); float *B = (float*) aligned_malloc(length * sizeof(float), 64); float *C = (float*) aligned_malloc(length * sizeof(float), 64); for(i=0; i<length; i++){ A[i] = 1; B[i] = 2; } float * pA = A; float * pB = B; float * pC = C; for(i=0; i<AVXLength; i++ ){ __asm__("vmovaps %1,%%zmm0\n" "vmovaps %2,%%zmm1\n" "vaddps %%zmm0,%%zmm0,%%zmm1\n" "vmovaps %%zmm0,%0;" : "=m" (pC) : "m" (pA), "m" (pB)); pA += 512; pB += 512; pC += 512; } return 0; } 

我使用gcc作为编译器(因为我没有钱买intel编译器)。 这是我的命令行来编译这个代码:

 k1om-mpss-linux-gcc add.c -o add.out 

问题出在内联程序集中。 下面的内联程序集修复了它。

 __asm__("vmovaps %1,%%zmm1\n" "vmovaps %2,%%zmm2\n" "vaddps %%zmm1,%%zmm2,%%zmm3\n" "vmovaps %%zmm3,%0;" : "=m" (*pC) : "m" (*pA), "m" (*pB)); 

如前所述,Knights Corner(KNC)没有AVX512。 不过,它确实有类似的东西。 事实证明,KNC vs AVX512的问题在这里是一个红鲱鱼。 问题在于OP内联汇编。

而不是使用内联汇编,我建议你使用内在函数。 在线英特尔内部指南介绍了KNC内部函数。

此外, CERN的PrzemysławKarpiński将Agner Fog的矢量类库扩展为使用KNC 。 你可以在这里找到git仓库。 如果您查看文件vectorf512_mic.h,您可以了解到很多关于KNC内在函数的知识。

我将你的代码转换为使用这些内在函数(在这种情况下与AVX512内部函数相同):

 int main(int argc, char* argv[]) { int i; const int length = 65536; const int AVXLength = length /16; float *A = (float*) aligned_malloc(length * sizeof(float), 64); float *B = (float*) aligned_malloc(length * sizeof(float), 64); float *C = (float*) aligned_malloc(length * sizeof(float), 64); for(i=0; i<length; i++){ A[i] = 1; B[i] = 2; } for(i=0; i<AVXLength; i++ ){ __m512 a16 = _mm512_load_ps(&A[16*i]); __m512 b16 = _mm512_load_ps(&B[16*i]); __m512 s16 = _mm512_add_ps(a16,b16); _mm512_store_ps(&C[16*i], s16); } return 0; } 

KNC内部函数仅由ICC支持。 但是,KNC带有Manycore平台软件堆栈(MCSS) ,它带有一个特殊版本的gcc, k1om-mpss-linux-gcc ,它可以像使用内联汇编一样使用KNC的AVX512功能。


KNC和AVX512的助记符在这种情况下是相同的。 因此我们可以使用AVX512内部函数来发现使用的程序集

 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 -O3 -mavx512 knc.c产生

 vmovaps (%rdi), %zmm0 vaddps (%rsi), %zmm0, %zmm0 vmovaps %zmm0, (%rdx) 

从这一个解决方案使用内联汇编将是

 __asm__("vmovaps (%1), %%zmm0\n" "vpaddps (%2), %%zmm0, %%zmm0\n" "vmovaps %%zmm0, (%0)" : : "r" (pC), "r" (pA), "r" (pB) : ); 

用前面的代码GCC为每个数组生成添加指令。 这是一个更好的解决方案,使用只生成一个添加的索引寄存器。

 for(i=0; i<length; i+=16){ __asm__ __volatile__ ( "vmovaps (%1,%3,4), %%zmm0\n" "vpaddps (%2,%3,4), %%zmm0, %%zmm0\n" "vmovaps %%zmm0, (%0,%3,4)" : : "r" (C), "r" (A), "r" (B), "r" (i) : "memory" ); } 

最新版本的MPSS(3.6)包括支持AVX512内部函数的GCC 5.1.1。 所以我认为,只要与KNC内在函数相同,只要使用内联汇编,就可以使用AVX512内在函数。 查看“英特尔内部指南”显示,在大多数情况下它们是相同的。