鲲鹏gcc -fsimdmath 矢量化数学库选项详解

举报
卜乐 发表于 2021/06/30 18:45:53 2021/06/30
【摘要】 本文将详细分析矢量化数学库的使能,以及鲲鹏gcc在这方面做得一些工作。

本文将详细分析矢量化数学库的使用,以及鲲鹏gcc在这方面做得一些工作。

背景
矢量化的概念自提出以来被广泛应用在各种计算场景中,其应用的边界也在不断拓展。在很多hpc应用中都存在一种场景,就是在循环中调用数学函数,这种情况下一般矢量化进程会被数学函数调用打断,从而只能生成标量代码,不能生成矢量化代码。

我们举个例子,假设有如下简单代码 test.c

void test (float a[], float b[], int len)
{
    for (int i = 0; i <len; i++) {
        a[i] = a[i] + b[i];
    }
}

使用gcc 9.3.0 gcc -O3 test.c -S -o test.s 编译之后可以得到一个矢量化版本的汇编代码。打开生成的test.s汇编文件可以看到生成了这段汇编

.L4:
        ldr     q0, [x0, x3]
        ldr     q1, [x1, x3]
        fadd    v0.4s, v0.4s, v1.4s
        str     q0, [x0, x3]
        add     x3, x3, 16
        cmp     x3, x4
        bne     .L4

可见已经生成了fadd v0.4s, v0.4s, v1.4s 这样的矢量化指令,每个迭代可以计算四个浮点数据。

那么我们看一下如果在循环中加入一个数学函数的话会怎样
test-expf.c:

#include <math.h>
void test (float a[], float b[], int len)
{
    for (int i = 0; i <len; i++) {
        a[i] = a[i] + expf(b[i]);
    }
}

使用 gcc test-expf.c -O3 -S -o test-expf.s 编译之后汇编如下

.L3:
        ldr     s8, [x20, x19]
        ldr     s0, [x22, x19]
        bl      expf
        fadd    s8, s8, s0
        str     s8, [x20, x19]
        add     x19, x19, 4
        cmp     x21, x19
        bne     .L3

我们可以发现现在循环已经无法矢量化了,一个迭代只能进行一组浮点值的计算。

如果使用gcc的优化dump选项-fdump-tree-vect-details 查看矢量化的具体原因可以在生成的test-expf.c.160t.vect文件中看到矢量化失败的原因是

test-expf.c:5:23: missed:  statement clobbers memory: _7 = expf (_6);
test-expf.c:4:5: missed:  not vectorized: loop contains function calls or data references that cannot be analyzed
test-expf.c:4:5: note:  ***** Analysis failed with vector mode VOID
test-expf.c:4:5: missed: couldn't vectorize loop

也就是expf的加入导致了矢量化分析的失败,编译器认为expf这个函数调用是无法矢量化的。

好,到目前,我们提出了问题,就是当循环里面有数学库函数调用时,无法矢量化,从而会导致性能的下降。

开源解决方案
那么有办法解决上面提到的问题吗?当然有。
目前业界常用的思路是对数学函数进行矢量化版本声明,这个声明本质上是告诉编译器,这个函数可以输入输出矢量化结构的数据,比如一次输入输出4组浮点数。这种声明的方法可以参考openmp的相关介绍。编译器获取这种声明之后可以根据声明生成GNU ABI 约定的接口,同时根据声明的矢量化版本宽度自动寻求矢量化机会。

这里回到我们的例子,为了使能矢量化,我们需要对expf这个函数添加矢量化声明

#include <math.h>

#pragma omp declare simd simdlen(4) notinbranch
float expf (float x);

void test (float a[], float b[], int len)
{
    for (int i = 0; i <len; i++) {
        a[i] = a[i] + expf(b[i]);
    }
}

添加之后我们再次构建并生成dump文件,构建指令需要添加-fopenmp-simd -fno-math-errno这两个选项。其中-fopenmp-simd 选项是告诉编译器,需要处理openmp simd相关的pragma。 -fno-math-errno 是告诉编译器不需要考虑数学函数产生exception的error number。 这里多讲一下,矢量化函数一般是无法产生error number的,所以编译器默认其无法产生error number, 如果是默认的-fmath-errno模式,则其不会生成矢量化版本函数。

gcc test-expf.c -O3 -S -o test-expf.s -fdump-tree-vect-details -fopenmp-simd -fno-math-errno

构建完成可以看到矢量化的汇编循环已经生成了。

.L4:
        ldr     q16, [x20, x19]
        ldr     q0, [x21, x19]
        bl      _ZGVnN4v_expf
        fadd    v16.4s, v16.4s, v0.4s
        str     q16, [x20, x19]
        add     x19, x19, 16
        cmp     x19, x23
        bne     .L4

dump文件test-expf.c.160t.vect 中可以看到对expf这块代码的分析是找到其simdclone,所以分析继续了

test-expf.c:8:5: note:   ==> examining statement: _7 = expf (_6);
test-expf.c:8:5: note:   vect_is_simple_use: operand *_5, type of def: internal
test-expf.c:8:5: note:   vect_is_simple_use: vectype vector(4) float
test-expf.c:8:5: missed:   function is not vectorizable.
test-expf.c:8:5: note:   vect_is_simple_use: operand *_5, type of def: internal
test-expf.c:8:5: note:   vect_is_simple_use: vectype vector(4) float
test-expf.c:8:5: note:    === vectorizable_simd_clone_call ===

编译阶段的问题就是这样解决了。有些同学可能注意到一个问题,就是编译接口是生成了,那这个矢量化的expf函数的实现在哪里呢?
这个问题的答案是最好使用业界已经成熟的矢量化函数,当然有兴趣也可以自己开发。目前性能最好的arm矢量化数学库是arm公司自己开发开源的mathlib,但是这个库的函数支持并不全面。另一个选择是sleef, 这个库的函数性能较差,但是支持很全面。

鲲鹏gcc -fsimdmath 选项
鲲鹏gcc在矢量化数学库的支持上面做了封装的工作。我们可以看到在开源的支持方式上面有三个问题。

  1. 接口声明很难写,需要非常熟练地掌握openmp相关知识和gnu abi相关知识。
  2. 选项添加很繁杂,不仅要添加使能pragma的选项,还要添加其他相关选项。
  3. 不同语言间的支持程度不同,使能需要的工作更加复杂。

鲲鹏gcc将以上这些工作都封装起来,使用一个-fsimdmath 选项去使能所有mathlib支持的矢量化数学库使能接口。同样是刚才的用例,我们可以使用

gcc test-expf.c -O3 -S -o test-expf.s -fsimdmath

编译指令生成矢量化数学库接口。此选项封装了所有使能所需的指令及相关openmp声明,并制定编译器去自动寻找相关声明。另外鲲鹏gcc内置了mathlib数学库,所以链接时指定-lmathlib 即可使用矢量化数学库。

不仅如此,鲲鹏gcc这个功能还尽量支持了常用语言,包括c\c++\fortran。当前支持矢量化的数学函数有

  • cos
  • cosf
  • sin
  • sinf
  • exp
  • expf
  • log
  • logf
  • pow
  • powf
  • exp2f

矢量化数学库使能选项-fsimdmath 就介绍到这里,各位有问题敬请留言。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。