2

我有一个代码,我在 Intel Xeon Phi Knights Landing (KNL) 7210(64 核)处理器(它是一台 PC,处于本机模式)上启动并使用 Intel c++ 编译器(icpc)版本 17.0.4。我还在英特尔酷睿 i7 处理器上启动了相同的代码,其中 icpc 的版本是 17.0.1。更正确地说,我在启动它的机器上编译代码(在 i7 上编译并在 i7 上启动,对于 KNL 相同)。我从不在一台机器上制作二进制文件并将其带到另一台机器上。使用 OpenMP 对循环进行并行化和矢量化。为了获得最佳性能,我使用英特尔编译器标志:

-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;
}
...

Particle p[N];- 一个粒子数组,Particle 是一个浮点结构。N - 最大粒子数。

如果要删除标志-march=native或将其替换为-march=knl-march=core-avx2,则一切正常。这个标志对程序做了坏事,但什么 - 我不知道。

我在网上找到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,但它们给出了相同的错误。

所以,我想问的是:

  1. 为什么-march=native-xMIC-AVX512不工作和-march=knl工作;是否-xMIC-AVX512包含在-march=nativeKNL 的标志中?

  2. 我可以在 KNL 上启动代码时替换标志-march=native-march=knl在 i7 上一切正常),它们是否等效?

  3. 如果使用英特尔编译器,为获得最佳性能而编写的标志集是最优的吗?

正如 Peter Cordes 所说,当程序在 GDB 中抛出浮点异常时,我将汇编器输出放在这里: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);
  ...
}

我应该强调,当我收到这个错误时,我已经在使用feenableexcept 了。我从程序调试开始就使用它,因为我们在代码中有错误(浮点异常)并且必须更正它们。

4

1 回答 1

1

feenableexcept用于取消屏蔽某些 FP 异常,因此创建无效临时结果的优化将使您的程序崩溃。

英特尔的编译器-fp-model fast=2, likegcc -ffast-math假设 FP 异常被屏蔽,因此它可能导致FE_INVALID一些临时计算中的一些 SIMD 元素,只要最后一切正常(例如,混合以修复 recip-sqrt 出错的元素)。我想这就是这里发生的事情。

如果您发布错误的实际指令的反汇编(而不是在该函数的最开始时使用一堆整数乘法),我们可以确切地找出是什么优化导致了什么无效临时,但通常您需要使用不那么激进的 FP 选项在编译打开 FP 异常的构建时。


根据英特尔的文档

-fp-model fast[=1|2] or /fp:fast[=1|2]

浮点异常语义默认是禁用的,并且它们不能被启用,因为你不能在同一个编译中同时指定 fast 和 except。要启用异常语义,您必须明确指定另一个关键字(有关详细信息,请参阅其他关键字描述)。

如果您希望编译器尊重 FP 异常是可见的副作用-fp-model except这一事实,则需要使用。 默认情况下启用。

如果你要调用修改 FP 环境的函数,ISO C 说你应该使用#pragma STDC FENV_ACCESS ON,否则,对 FP 环境的修改就没有“意义”。“否则,实现可以自由假设浮点控制模式始终是默认模式,并且从未测试或修改浮点状态标志。” 我不确定启用异常是否真的很重要。可能并不重要,只要您在程序启动时执行一次,否则计算是否在启用异常之前或之后发生就很重要。


同样,对于 gcc,-ffast-mathincludes-fno-trapping-math向编译器承诺 FP 指令不会引发 SIGFPE,只是在 MXCSR 中静默设置粘性状态位并产生 NaN(无效)、+-Infinity(上溢)或0.0(下溢)。

于 2018-10-02T08:05:04.737 回答