openmp - 如何使用 openmp 生成数学函数 "exp"的 simd 代码?

标签 openmp simd

我有一个简单的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 代码。有人可以帮忙吗?

最佳答案

您可以使用以下四种替代方法之一来矢量化 exp 函数。 请注意,我使用了 expf(浮点)而不是 exp,它是一个 double 函数。 这个Godbolt link显示这些函数已向量化:在编译器生成的代码中搜索 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; 
}

请注意Peter Cordes' comment在这里非常相关: 函数 _ZGVdN8v___expf_finite 给出的结果可能与 expf 略有不同 因为它的重点是速度,而不是特殊情况,例如输入 无限、次正规或不是数字。 而且,精度为4-ulp最大相对误差, 这可能比标准 expf 函数的准确度稍低。 因此,您需要优化级别 -Ofast (这允许不太准确的代码) 而不是 -O3 来使用 gcc 获取代码矢量化。

参见this libmvec page了解更多详情。

以下测试代码在 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;
}

关于openmp - 如何使用 openmp 生成数学函数 "exp"的 simd 代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53280563/

相关文章:

c++ - OpenMP,并行 for 循环,bigIntegers

c - 为 OpenMP 中的每个内部循环启动一个线程

c++ - OpenMP 正确声明变量

c++ - 递归函数上的 OpenMP 并行化

c++ - 从 WinAPI 线程调用 omp_set_num_threads 时出现问题

intel - 英特尔为什么不以更加兼容或通用的方式设计其SIMD ISA?

c++ - Visual Studio 编译器标志/架构和性能

assembly - 如何在 AVX/AVX2 中增加向量

c++ - 打包和解交错两个 __m256 寄存器

c - 从 SSE vector 中提取标量值