覆盖来自 SVML 的函数调用
overriding function calls from SVML
Xeon-Phi Knights Landing 内核具有快速 exp2
指令 vexp2pd
(内在 _mm512_exp2a23_pd
)。英特尔 C++ 编译器可以使用编译器附带的短向量数学库 (SVML) 对 exp
函数进行向量化。具体来说,它调用函数 __svml_exp8
.
但是,当我单步执行调试器时,我没有看到 __svml_exp8
使用 vexp2pd
指令。它是一个具有许多 FMA 操作的复杂功能。我知道 vexp2pd
不如 exp
准确,但如果我使用 -fp-model fast=1
(默认值)或 fp-model fast=2
我希望编译器使用此指令但它不会。
我有两个问题。
- 有没有办法让编译器使用
vexp2pd
?
- 如何安全地覆盖对
__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);
}
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 , %rax
cmpq %r8, %rax
jb ..B1.12
请务必理解准确性影响,因为不仅最终结果只有大约 22 位准确,而且 vexp2pd 还会将任何非规范化结果刷新为零,而不管 MXCSR 中设置的 FTZ/DAZ 位。
第二个问题:"How do I safely override the call to __svml_exp8?"
您的方法通常不安全。 SVML 例程是英特尔编译器内部的,并且依赖于自定义调用约定,因此具有相同名称的通用例程可能会破坏比库例程更多的寄存器,并且您最终可能会遇到难以调试的 ABI 不匹配。
提供自己的向量函数的更好方法是使用#pragma omp declare simd,例如参见 https://software.intel.com/en-us/node/524514 and possibly the vector_variant attribute if prefer coding with intrinsics, see https://software.intel.com/en-us/node/523350。只是不要尝试覆盖标准数学名称,否则会出错。
Xeon-Phi Knights Landing 内核具有快速 exp2
指令 vexp2pd
(内在 _mm512_exp2a23_pd
)。英特尔 C++ 编译器可以使用编译器附带的短向量数学库 (SVML) 对 exp
函数进行向量化。具体来说,它调用函数 __svml_exp8
.
但是,当我单步执行调试器时,我没有看到 __svml_exp8
使用 vexp2pd
指令。它是一个具有许多 FMA 操作的复杂功能。我知道 vexp2pd
不如 exp
准确,但如果我使用 -fp-model fast=1
(默认值)或 fp-model fast=2
我希望编译器使用此指令但它不会。
我有两个问题。
- 有没有办法让编译器使用
vexp2pd
? - 如何安全地覆盖对
__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);
}
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 , %rax
cmpq %r8, %rax
jb ..B1.12
请务必理解准确性影响,因为不仅最终结果只有大约 22 位准确,而且 vexp2pd 还会将任何非规范化结果刷新为零,而不管 MXCSR 中设置的 FTZ/DAZ 位。
第二个问题:"How do I safely override the call to __svml_exp8?" 您的方法通常不安全。 SVML 例程是英特尔编译器内部的,并且依赖于自定义调用约定,因此具有相同名称的通用例程可能会破坏比库例程更多的寄存器,并且您最终可能会遇到难以调试的 ABI 不匹配。
提供自己的向量函数的更好方法是使用#pragma omp declare simd,例如参见 https://software.intel.com/en-us/node/524514 and possibly the vector_variant attribute if prefer coding with intrinsics, see https://software.intel.com/en-us/node/523350。只是不要尝试覆盖标准数学名称,否则会出错。