2017-03-07 173 views
2

Xeon-Phi骑士登陆核心有一个快速exp2指令vexp2pd(内部_mm512_exp2a23_pd)。英特尔C++编译器可以使用编译器附带的短矢量数学库(SVML)矢量化exp函数。具体而言,它称为功能__svml_exp8从SVML覆盖函数调用

然而,当我通过调试步骤我没有看到__svml_exp8使用vexp2pd指令。这是许多FMA操作的复杂功能。据我所知,vexp2pdexp不太准确,但如果我用-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); 
} 
+0

你问的编译器使用'vexp2pd'扔的精度有30位。即使快速数学也不会这样做。 – Mysticial

+0

@Mysticial我很确定我注意到编译器使用'vrcp28pd'(实际上你可以在这里看到它(https://godbolt.org/g/Wya9Ic))。所以如果它使用快速互惠为什么不是一个快速的'exp'?看看这个代码的倒数,它看起来可能是在做一次牛顿迭代或其他事情(否则,所有FMA都会出现这种情况)。这可以解释为什么它使用快速倒数而不是快速的'exp'。 –

+0

我从来没有听说过'vfixupimmpd'。奇怪的指示。内在指导说它需要AVXVL,但是共享者为没有AVXVL的KNL生成它。 –

回答

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域排阻编译= 1 -fimf精度位= 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位准确,但是vexp2pd也会刷新到任何非规格化结果为零,而不管MXCSR中设置的FTZ/DAZ位。

对第二个问题:“我如何安全地重写调用__svml_exp8?” 您的方法通常不安全。 SVML例程是英特尔编译器的内部函数,并且依赖于自定义调用约定,因此与库例程相比,具有相同名称的泛型例程可能会更多地注册更多的寄存器,并且最终会导致难以调试的ABI不匹配。

提供自己的载体功能,更好的办法是利用OMP的#pragma声明SIMD,例如请参阅https://software.intel.com/en-us/node/524514,如果喜欢使用intrinsics进行编码,请参阅https://software.intel.com/en-us/node/523350,可能还有vector_variant属性。只是不要试图覆盖标准的数学名称,否则你会得到一个错误。

+0

您的代码不会为ICC 17生成'vexp2pd'。我尝试了很多变体。但是如果我在你的答案的代码中用'exp2'替换'exp',那么它确实会产生'vexp2pd'。 –

+1

请试用18.0(目前处于测试阶段) – NikitaA