我编写了一个代码,在xeon phi英特尔协处理器上使用knc指令(512bit长向量)添加两个数组。不过,我在内联汇编部分有分段部分。
这是我的代码:
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作为编译器(因为我没有钱买英特尔编译器)。这是我编译这段代码的命令行:
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));
最佳答案
由于already explained,骑士角(knights corner,knc)没有avx512。然而,它确实有类似的东西。事实证明,knc vs avx512的问题是个棘手的问题。问题出在ops内联程序集中。
我建议您使用内部函数,而不是使用内联程序集。knc内部函数在Intel Intrinsic Guide online中描述。
此外,Przemysław Karpiński at CERN extend Agner Fog's Vector Class Library to use KNC。您可以找到git存储库here。如果您查看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附带了一个特殊版本的gcc
k1om-mpss-linux-gcc
,它可以使用knc的avx512类特性,使用内联汇编。在这种情况下,knc和avx512的mnemoncis是相同的。因此,我们可以使用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为每个数组生成add指令。这里有一个更好的解决方案,使用只生成一个add的索引寄存器。
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。因此,我认为只要avx512内部函数与knc内部函数相同,就可以使用它们,只有当它们不一致时才可以使用内联程序集。查看《英特尔内部指南》可以发现,它们在大多数情况下都是相同的。
关于c - vmovaps的段错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34148636/