Как сгенерировать код simd для математической функции "exp", используя openmp? - PullRequest
0 голосов
/ 13 ноября 2018

У меня есть простой код c следующим образом

void calculate_exp(float *out, float *in, int size) {
    for(int i = 0; i < size; i++) {
        out[i] = exp(in[i]);
    }
}

Я хотел оптимизировать его, используя open-mp simd. Я новичок в open-mp и использовал несколько прагм, таких как «omp simd», «omp simd safelen» и т. Д. Но я не могу сгенерировать код simd. Кто-нибудь может помочь?

1 Ответ

0 голосов
/ 13 ноября 2018

Вы можете использовать один из следующих четырех вариантов для векторизации функции exp.Обратите внимание, что я использовал expf (float) вместо exp, что является функцией double.Эта ссылка Годболта показывает, что эти функции векторизованы: найдите call _ZGVdN8v___expf_finite в сгенерированном компилятором коде.

#include<math.h>

int exp_vect_a(float* x, float* y, int N) {
    /* Inform the compiler that N is a multiple of 8, this leads to shorter code */
    N = N & 0xFFFFFFF8;    
    x = (float*)__builtin_assume_aligned(x, 32); /* gcc 8.2 doesn't need aligned x and y  to generate `nice` code */
    y = (float*)__builtin_assume_aligned(y, 32); /* with gcc 7.3 it improves the generated code                   */
    #pragma omp simd             
    for(int i=0; i<N; i++) y[i] = expf(x[i]);
    return 0; 
}


int exp_vect_b(float* restrict x, float* restrict y, int N) {
    N = N & 0xFFFFFFF8;
    x = (float*)__builtin_assume_aligned(x, 32); /* gcc 8.2 doesn't need aligned x and y  to generate `nice` code */
    y = (float*)__builtin_assume_aligned(y, 32); /* with gcc 7.3 it improves the generated code                   */
    for(int i=0; i<N; i++) y[i] = expf(x[i]);
    return 0; 
}

/* This also vectorizes, but it doesn't lead to `nice` code */
int exp_vect_c(float* restrict x, float* restrict y, int N) {
    for(int i=0; i<N; i++) y[i] = expf(x[i]);
    return 0; 
}

/* This also vectorizes, but it doesn't lead to `nice` code */
int exp_vect_d(float* x, float* y, int N) {
    #pragma omp simd             
    for(int i=0; i<N; i++) y[i] = expf(x[i]);
    return 0; 
}

Обратите внимание, что Комментарий Питера Кордеса оченьздесь уместно: функция _ZGVdN8v___expf_finite может давать результаты, немного отличающиеся от expf, потому что она фокусируется на скорости, а не на особых случаях, таких как бесконечные, ненормальные или не входные числа.Более того, точность составляет максимальную относительную ошибку 4-ульп, которая, вероятно, немного менее точна, чем стандартная функция expf.Поэтому вам необходим уровень оптимизации -Ofast (который позволяет использовать менее точный код) вместо -O3, чтобы получить векторизацию кода с помощью gcc.

См. эту страницу libmvec для получения дополнительной информации.

Следующий тестовый код компилируется и успешно работает с gcc 7.3:

#include <math.h>
#include <stdio.h>
/* gcc expv.c -m64 -Ofast -std=c99 -march=skylake -fopenmp -lm */

int exp_vect_d(float* x, float* y, int N) {
    #pragma omp simd             
    for(int i=0; i<N; i++) y[i] = expf(x[i]);
    return 0; 
}

int main(){
    float x[32];
    float y[32];
    int i;
    int N = 32;

    for(i = 0; i < N; i++) x[i] = i/100.0f;
    x[10]=-89.0f;            /* exp(-89.0f)=2.227e-39 which is a subnormal number */
    x[11]=-1000.0f;          /* output: 0.0                                   */
    x[12]=1000.0f;           /* output: Inf.                                  */
    x[13]=0.0f/0.0f;         /* input: NaN: Not a number                      */
    x[14]=1e20f*1e20f;       /* input: Infinity                               */
    x[15]=-1e20f*1e20f;      /* input: -Infinity                              */
    x[16]=2.3025850929940f;  /* exp(2.3025850929940f)=10.0...                 */
    exp_vect_d(x, y, N);
    for(i = 0; i < N; i++) printf("x=%11.8e,  y=%11.8e\n", x[i], y[i]);
    return 0;
}
...