我有一个代码,我在Intel Xeon Phi Knights Landing(KNL)7210(64核)处理器(它是PC,本机模式)上启动并使用Intel c编译器(icpc)版本17.0.4 . 此外,我在英特尔酷睿i7处理器上启动了相同的代码,其中icpc的版本是17.0.1 . 为了更正确,我在我启动它的机器上编译代码(在i7上编译并在i7上启动,对于KNL也是如此) . 我从不在一台机器上制作二进制文件并将其带到另一台机器上 . 使用OpenMP对循环进行并行化和矢量化 . 为了获得最佳性能,我使用了intel编译器标志:
-DCMAKE_CXX_COMPILER="-march=native -mtune=native -ipo16 -fp-model fast=2 -O3 -qopt-report=5 -mcmodel=large"
在i7上一切正常 . 但是在KNL上,代码可以使用 -march=native
,如果要添加此选项,程序会立即抛出浮点异常 . 如果用唯一的标志"-march=native"编译,情况是一样的 . 如果要使用gdb,它指向该段代码的 pp+=alpha/rd
行:
...
the code above is run in 1 thread
double K1=0.0, P=0.0;
#pragma omp parallel for reduction(+:P_x,P_y,P_z, K1,P)
for(int i=0; i<N; ++i)
{
P_x+=p[i].vx*p[i].m;
P_y+=p[i].vy*p[i].m;
P_z+=p[i].vz*p[i].m;
K1+=p[i].vx*p[i].vx+p[i].vy*p[i].vy+p[i].vz*p[i].vz;
float pp=0.0;
#pragma simd reduction(+:pp)
for(int j=0; j<N; ++j) if(i!=j)
{
float rd=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y)+(p[i].z-p[j].z)*(p[i].z-p[j].z));
pp+=alpha/rd;
}
P+=pp;
}
...
粒子 p[N];
- 一个粒子阵列,粒子是浮子的结构 . N - 粒子的最大数量 .
如果删除标志 -march=native
或用 -march=knl
或 -march=core-avx2
替换它,一切都很好 . 这个标志对程序做了坏事,但是什么 - 我不知道 .
我发现在Internet(https://software.intel.com/en-us/articles/porting-applications-from-knights-corner-to-knights-landing,https://math-linux.com/linux/tip-of-the-day/article/intel-compilation-for-mic-architecture-knl-knights-landing)中应该使用标志: -xMIC-AVX512
. 我试图使用这个标志和 -axMIC-AVX512
,但他们给出了同样的错误 .
所以,我想问的是:
-
为什么
-march=native
,-xMIC-AVX512
不起作用-march=knl
有效;是-xMIC-AVX512
包含在KNL的-march=native
标志中? -
当我在KNL上启动代码时,我可以用
-march=knl
替换标志-march=native
(在i7上一切正常),它们是否相同? -
如果使用英特尔编译器,是否为最佳性能编写了最佳标志集?
正如,Peter Cordes所说,当程序在GDB中抛出浮点异常时,我在这里放置了assembeler输出:1)(gdb)disas的输出:
Program received signal SIGFPE, Arithmetic exception.
0x000000000040e3cc in randomizeBodies() ()
Missing separate debuginfos, use: debuginfo-install libgcc-4.8.5-
16.el7.x86_64 libstdc++-4.8.5-16.el7.x86_64
(gdb) disas
Dump of assembler code for function _Z15randomizeBodiesv:
0x000000000040da70 <+0>: push %rbp
0x000000000040da71 <+1>: mov %rsp,%rbp
0x000000000040da74 <+4>: and $0xffffffffffffffc0,%rsp
0x000000000040da78 <+8>: sub $0x100,%rsp
0x000000000040da7f <+15>: vpxor %xmm0,%xmm0,%xmm0
0x000000000040da83 <+19>: vmovups %xmm0,(%rsp)
0x000000000040da88 <+24>: vxorpd %xmm5,%xmm5,%xmm5
0x000000000040da8c <+28>: vmovq %xmm0,0x10(%rsp)
0x000000000040da92 <+34>: mov $0x77359400,%ecx
0x000000000040da97 <+39>: xor %eax,%eax
0x000000000040da99 <+41>: movabs $0x5deece66d,%rdx
0x000000000040daa3 <+51>: mov %ecx,%ecx
0x000000000040daa5 <+53>: imul %rdx,%rcx
0x000000000040daa9 <+57>: add $0xb,%rcx
0x000000000040daad <+61>: mov %ecx,0x9a3b00(,%rax,8)
0x000000000040dab4 <+68>: mov %ecx,%esi
0x000000000040dab6 <+70>: imul %rdx,%rsi
0x000000000040daba <+74>: add $0xb,%rsi
0x000000000040dabe <+78>: mov %esi,0x9e3d00(,%rax,8)
0x000000000040dac5 <+85>: mov %esi,%edi
0x000000000040dac7 <+87>: imul %rdx,%rdi
0x000000000040dacb <+91>: add $0xb,%rdi
0x000000000040dacf <+95>: mov %edi,0xa23f00(,%rax,8)
0x000000000040dad6 <+102>: mov %edi,%r8d
0x000000000040dad9 <+105>: imul %rdx,%r8
0x000000000040dadd <+109>: add $0xb,%r8
0x000000000040dae1 <+113>: mov %r8d,0xa64100(,%rax,8)
0x000000000040dae9 <+121>: mov %r8d,%r9d
0x000000000040daec <+124>: imul %rdx,%r9
0x000000000040daf0 <+128>: add $0xb,%r9
0x000000000040daf4 <+132>: mov %r9d,0xaa4300(,%rax,8)
0x000000000040dafc <+140>: mov %r9d,%r10d
0x000000000040daff <+143>: imul %rdx,%r10
0x000000000040db03 <+147>: add $0xb,%r10
0x000000000040db07 <+151>: mov %r10d,0x9a3b04(,%rax,8)
0x000000000040db0f <+159>: mov %r10d,%r11d
0x000000000040db12 <+162>: imul %rdx,%r11
0x000000000040db16 <+166>: add $0xb,%r11
0x000000000040db1a <+170>: mov %r11d,0x9e3d04(,%rax,8)
0x000000000040db22 <+178>: mov %r11d,%ecx
0x000000000040db25 <+181>: imul %rdx,%rcx
0x000000000040db29 <+185>: add $0xb,%rcx
0x000000000040db2d <+189>: mov %ecx,0xa23f04(,%rax,8)
2)p $ mxcsr的输出:
(gdb) p $mxcsr
1 = [ ZE PE DAZ DM PM FZ ]
3)p $ ymm0.v8_float的输出:
$2 = {3, 3, 3, 3, 3, 3, 3, 3}
4)p $ zmm0.v16_float的输出:
gdb) p $zmm0.v16_float
$3 = {3 <repeats 16 times>}.
我还提到要检测我使用标准的浮点异常
void handler(int sig)
{
printf("Floating Point Exception\n");
exit(0);
}
...
int main(int argc, char **argv)
{
feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW);
signal(SIGFPE, handler);
...
}
我应该强调 i have already been using feenableexcept when i got this error . 我从程序调试开始就使用它,因为我们在代码中有错误(浮点异常)并且必须纠正它们 .
1 回答
您使用
feenableexcept
来取消屏蔽某些FP异常,因此创建无效临时结果的优化会使程序崩溃 .英特尔与
-fp-model fast=2
的编译器,如gcc -ffast-math
,假设FP异常被屏蔽,因此它可以在某些临时计算中导致某些SIMD元素中的FE_INVALID
,只要一切都在最后解决(例如,混合以修复recip-sqrt出错的元素) ) . 我'd assume that'这是怎么回事 .如果你发布了故障的实际指令的反汇编(而不是在该函数的最开始时的一堆整数乘法),我们可以确切地知道什么是优化导致什么是无效的临时,但一般来说你需要使用不太激进的FP选项在编译打开FP异常的构建时 .
根据Intel's documentation:
You need to use -fp-model except if you want the compiler to respect the fact that FP exceptions are a visible side-effect. 默认情况下不启用 .
如果您打算调用修改FP环境的函数,ISO C表示您应该使用#pragma STDC FENV_ACCESS ON,如果不这样做,对FP环境的修改不能确定启用异常是否真的很重要 . 可能并不重要,只要您在程序启动时执行一次,否则在启用异常之前或之后计算是否发生都很重要 .
类似地,对于gcc,
-ffast-math
包括-fno-trapping-math
,它承诺编译器FP指令不会引发SIGFPE,只是在MXCSR中静默设置粘滞状态位并产生NaN(无效),-Infinity(溢出)或0.0
(下溢) .