переопределение вызовов функций из SVML

У ядер Xeon-Phi Knights Landing есть быстрый exp2 инструкция vexp2pd (внутренняя _mm512_exp2a23_pd). Компилятор Intel C ++ может векторизовать exp функция с использованием библиотеки Short Vector Math Library (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);
}

2

Решение

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-precision-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 бит, но и vexp2pd сбрасывает на ноль любые денормализованные результаты независимо от битов FTZ / DAZ, установленных в MXCSR.

На второй вопрос: «Как безопасно переопределить вызов __svml_exp8?»
Ваш подход, как правило, небезопасен. Подпрограммы SVML являются внутренними для компилятора Intel и основаны на пользовательских соглашениях о вызовах, поэтому универсальная подпрограмма с тем же именем может потенциально заглушить большее количество регистров, чем библиотечная подпрограмма, и вы можете столкнуться с трудностью отладки при несовпадении ABI.

Лучшим способом предоставления собственных векторных функций будет использование #pragma omp Declare Simd, например увидеть https://software.intel.com/en-us/node/524514 и, возможно, атрибут vector_variant, если предпочитаете кодирование с помощью встроенных функций, см. https://software.intel.com/en-us/node/523350. Только не пытайтесь переопределить стандартные математические имена, иначе вы получите ошибку.

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]