2

Xeon-Phi Knights Landing 内核具有快速exp2指令vexp2pd(固有_mm512_exp2a23_pd)。英特尔 C++ 编译器可以exp使用编译器附带的短向量数学库 (SVML) 对函数进行向量化。具体来说,它调用了函数__svml_exp8

但是,当我单步执行调试器时,我看不到它__svml_exp8使用了该vexp2pd指令。这是一个具有许多 FMA 操作的复杂函数。我知道这vexp2pd不如exp但如果我使用-fp-model fast=1(默认值)或fp-model fast=2我希望编译器使用此指令但它没有。

我有两个问题。

  1. 有没有办法让编译器使用vexp2pd
  2. 如何安全地覆盖对 的调用__svml_exp8

至于第二个问题,这是我迄今为止所做的。

//exp(x) = exp2(log2(e)*x)  
extern "C" __m512d __svml_exp8(__m512d x) {        
    return _mm512_exp2a23_pd(_mm512_mul_pd(_mm512_set1_pd(M_LOG2E), x));
}

这安全吗?有没有更好的解决方案,例如内联函数?在下面的测试代码中,这比我不覆盖时快大约 3 倍。

//https://godbolt.org/g/adI11c
//icpc -O3 -xMIC-AVX512 foo.cpp
#include <math.h>
#include <stdio.h>
#include <x86intrin.h>

extern "C" __m512d __svml_exp8(__m512d x) {
  //exp(x) = exp2(log2(e)*x)  
  return _mm512_exp2a23_pd(_mm512_mul_pd(_mm512_set1_pd(M_LOG2E), x));
}

void foo(double * __restrict x, double * __restrict y) {
  __assume_aligned(x, 64);
  __assume_aligned(y, 64);
  for(int i=0; i<1024; i++) y[i] = exp(x[i]);
}

int main(void) {
  double x[1024], y[1024];
  for(int i=0; i<1024; i++) x[i] = 1.0*i;
  for(int r=0; r<1000000; r++) foo(x,y);
  double sum=0;
  //for(int i=0; i<1024; i++) sum+=y[i];
  for(int i=0; i<8; i++) printf("%f ", y[i]); puts("");
  //printf("%lf",sum);
}
4

1 回答 1

4

ICC 将生成 vexp2pd,但仅在目标 -fimf* 开关指定的非常宽松的数学要求下。

#include <math.h>

void vfoo(int n, double * a, double * r)
{
    int i;
    #pragma simd
    for ( i = 0; i < n; i++ )
    {
        r[i] = exp(a[i]);
    }
}

例如使用 -xMIC-AVX512 -fimf-domain-exclusion=1 -fimf-accuracy-bits=22 编译

..B1.12:
        vmovups   (%rsi,%rax,8), %zmm0
        vmulpd    .L_2il0floatpacket.2(%rip){1to8}, %zmm0, %zmm1
        vexp2pd   %zmm1, %zmm2
        vmovupd   %zmm2, (%rcx,%rax,8)
        addq      $8, %rax
        cmpq      %r8, %rax
        jb        ..B1.12

请务必了解准确性的含义,因为不仅最终结果只有大约 22 位准确,而且无论 MXCSR 中设置的 FTZ/DAZ 位如何,vexp2pd 还会将任何非规范化结果刷新为零。

对于第二个问题:“如何安全地覆盖对 __svml_exp8 的调用?” 您的方法通常不安全。SVML 例程是英特尔编译器的内部程序,并且依赖于自定义调用约定,因此同名的通用例程可能会比库例程破坏更多的寄存器,并且最终可能会导致难以调试的 ABI 不匹配。

提供您自己的向量函数的更好方法是使用#pragma omp declare simd,例如,请参阅https://software.intel.com/en-us/node/524514,如果更喜欢使用内在函数编码,可能还有 vector_variant 属性,请参阅https://software.intel.com/en-us/node/523350。只是不要尝试覆盖标准数学名称,否则会出错。

于 2017-03-09T22:24:13.117 回答