【毕昇编译器】编译优化与软硬协同释放鲲鹏澎湃算力 笔记

一、背景知识——编译优化与软硬协同

编译器:将源程序(高级语言)转换为等价的机器语言

  • 源代码(source code)→预处理器(preprocessor)→编译器(compiler)→目标代码(object code)→链接器(Linker)→可执行程序(executables)

编译优化:保留程序语义(正确性)的前提下,对程序进行等价变化,从而较少程序运行时间

软硬协同:在编译优化中适配/使能诸多硬件特性

      HPC( High Performance Computing,高性能计算)领域主要是解决计算密集型、海量数据处理等业务的计算需求,如科学研究、气象预报、计算模拟等。如何提高计算能力、极致化应用性能成为当前 HPC 领域各大平台最关键的课题之一,编译器在其中发挥着至关重要的作用。

二、认识毕昇编译器

      毕昇编译器是基于开源LLVM 10.0.1版本开发,并进行了优化和改进,同时将flang作为默认的Fortran语言前端编译器,是一种Linux下针对鲲鹏920的高性能编译器,其针对鲲鹏平台进行了深度优化的高性能编译器。除支持 LLVM 通用功能和优化外之外,对中端及后端的关键技术点进行了深度优化,对以下三个方面进行了增强,使得鲲鹏平台的强劲算力能够最大限度地得到释放。

  • 高性能编译算法:编译深度优化,内存优化增强,自动矢量化等,大幅提升指令和数据呑吐量。
  • 加速指令集:结合 NEON/SVE 等内嵌指令技术,深度优化指令编译和运行时库,发挥鲲鹏架构极致算力。
  • AI 迭代调优:内置 AI 自学模型,自动优化编译配置,迭代提升程序性能,完成最优编译。

关键特性

  1. 支持鲲鹏微架构芯片及指令优化

  2. 通过软硬协同提供相较开源LLVM更高的性能

  3. 集成Auto-tuner特性支持编译器自动调优

运行平台:鲲鹏920硬件平台

操作系统:openEuler21.03、openEuler 20.03 (LTS)、CentOS 7.6、Ubuntu 18.04、Ubuntu 20、麒麟V10、UOS 20

注:详细的运行平台和操作系统对应关系请参见 兼容性查询工具

部分通用信息请参考LLVM的用户指导https://llvm.org/docs/UserGuides.html

相比GCC和icc,LLVM前端Clang对语法的检查更严谨,严格匹配语言标准,Clang的常见兼容性和可移植性问题,请参考开源官方文档https://clang.llvm.org/compatibility.html

支持的编程语言

      LLVM是一种涵盖多种编程语言和目标处理器的编译器,毕昇编译器聚焦于对C、C++、Fortran语言的支持,利用LLVM的Clang作为C和C++的编译和驱动程序,Flang作为Fortran语言的编译和驱动程序。

1、C,C++程序

      Clang不仅仅是可以将C, C++程序编译为LLVM中间表示的IR,它也是一个驱动程序,会调用所有以代码生成为目标的LLVM优化遍,直到生成最终的二进制文件。毕昇编译器提供了端到端编译程序所需的所有工具和库。

2、Fortran程序

      Flang是专为LLVM集成而设计的Fortran前端,由两个组件flang1和flang2组成。它也是一个驱动程序,将源代码转换为LLVM IR,前端驱动程序将IR传输下去进行优化和目标代码生成。

毕昇编译器优化效果:Benchmark跑分、HPC典型应用性能提升

      当前毕昇编译器已广泛应用于多种 HPC 典型场景,如气象、安防、流体力学等,性能优势已初步体现。毕昇编译器与鲲鹏芯片协同,充分发挥芯片的性能,提升鲲鹏硬件平台上业务的性能体验。

基于鲲鹏上编译器优化,SPEC CPU 2017 benchmark 跑分平均优于 GCC 20%以上

HPC 典型气象应用 WRF 优于 GCC 10%~20%

三、毕昇编译器典型优化场景及其优化原理

1、结构体内存布局优化——大幅提升缓存命中率,突破访存瓶颈

      SPEC CPU 2017 benchmark 中的 mcf 子项是对内存要求极高的应用,它是一款叫做MCF的大规模交通规划软件的核心代码。其瓶颈代码如下图左边所示。

将结构体数组转换为数组结构体

结构体可以是显式的,也可以通过检查循环中的数组使用情况来推断它们

      可见在 struct 中,data1 的使用率极高,而 data2 是不使用的。然而由于源代码中,数据的排布是以结构体数组的形式排布。按照一般编译器的编译方式,拿数据时每次都会将整个结构体放到 cache 里面,导致大量不参与计算的 data2 也被加载到了 cache 中,造成高速内存空间的浪费和性能的损耗。毕昇编译器会通过用户标记的结构体声明,或者通过自动检查循环中适合优化的内存场景,确认优化点。然后通过将结构体数组变为数组结构体的方式(如上图右),将有效数据紧凑排布,从而提高 cache 命中率和应用性能。经测试,此优化可以对 mcf 子项带来50%的性能提升。

2、结构体指针压缩优化——大幅降低内存使用,提升缓存命中率

  • 使编译过程更加灵活和可控
  • 细粒度编译控制,提供更多优化机会

    将指针成员由8字节压缩至4字节,减小每个结构体node的内存体积。被压缩的域成员指针外提为全局结构体指针ps_head,ps变换为相对基址的偏移,将有效数据紧凑排布,从而大幅降低内存使用,提升 cache 命中率和应用性能

3、软件预取——大幅提高程序性能,提升缓存命中率

(1)软件预取:通过插入预取指令提前从内存中读取所需数据

(2)预取的效果取决于“提前量”

  • 数据太早到达→浪费宝贵的cache空间
  • 数据太晚到达→仍需要等待访存过程

(3)如何计算“提前量"

  • 循环大小
  • Cache line大小
  • 访存延迟

(4)针对鲲鹏微架构特征调整软件预取参数

4、自动矢量化—计算效率提升的秘诀

使用矢量寄存器、矢量指令提高并行度

  • 理论基础:single instruction multiple data,一条指令可以处理多个数据
  • 硬件基础:ARM NEON指令集扩展,32个128位的矢量寄存器,指令可以同时操作4*32或2*64的数据
  • 软件基础:编译器针对NEON指令的分析和优化

      鲲鹏平台支持 Armv8 NEON 矢量化指令集。当前支持32个128位的矢量寄存器,指令可以同时操作4*32或2*64的数据。毕昇编译器依托这种硬件优势做了大量优化,包括 SLP(superword-level parallelism) 矢量化和循环自动矢量化

      例如在 SPEC CPU 2017 benchmark 中处理视频流格式转换的x264子项中,毕昇编译器会自动识别并使用 uabd 和 udot 这类高效向量指令完成计算来替换标量指令,增大单时钟周期的数据处理量, 从而大幅提升计算效率。对于 x264 子项,这项优化可有效提升其30%的计算效率。

5、循环优化——帮助发现更多的优化机会

Loop unroll:将循环体复制多遍

  • 减少分支跳转次数
  • 帮助发现更多的优化机会

Loop (partial) unswitch:外提(减少)循化内条件判断

Loop fusion/distribution:将循环体合成一个/拆成多个

6、Autotuner—基于机器学习快速获取最优编译配置

      如何获取性能最优编译选项是编译器使用中常见的问题,往往需要长时间的手动选项调优。为了减少这其中的工作量,使得用户能快速找到最优的优化选项,毕昇编译器自研了基于 ML 的自动搜索技术(ML-based Search) 的 Autotuner 工具

      一种自动化的迭代过程, 通过操作编译设置来优化给定程序,以实现最佳性能。它由两个组件配合完成,毕异编译器和Autotuner命令行工具。此功能不需要在源代码中注入pragma,而是允许用户在简单的yaml文件中指定优化配置,该文件包含优化信息及其相应的代码区域信息,包括名称和行号。此外,它还可以记录优化结果,以及可调优的代码区间并以yaml的形式导出。

  • 与毕昇编译器进行交互:
    • 根据编译器产生的可调优代码结构创建搜索空间(search space)
    • 生成编译配置并调用编译器来编译源代码
  • 操作调优参数以及应用搜索算法
    • 自带的遗传算法
  • 获取性能数据

引入基于ML的自动搜索技术(ML-basedSearch)

关键技术点:

1、知识库(Autotuner Database):根据静动态分析信息,建立知识库,支持决策系统进行优化

2、优化决策系统(Optimal configuration):根据热点和性能评估信息、知识库信息,综合考虑确定优化措施

3、热点标记和性能评价:热点标记,瓶颈检测,性能评估

4、查找驱动(Feedback):将优化决策系统,反馈的优化建议

Autotuner 的调优流程由两个阶段组成:初始编译阶段(initial compilation)和调优阶段(tuning process),如下图所示:

初始编译阶段

      初始编译阶段发生在调优开始之前,Autotuner首先会让编译器对目标程序代码做一次编译,在编译的过程中,毕昇编译器会生成一些包含所有可调优结构的YAML文件, 告诉我们在这个目标程序中哪些结构可以用来调优,比如文件(module), 函数(function), 循环(loop)。 例如,循环展开是编译器中最常见的优化方法之一,它通过多次复制循环体代码,达到增大指令调度的空间,减少循环分支指令的开销等优化效果。若以循环展开次数(unroll factor)为对象进行调优,编译器会在YAML文件中生成所有可被循环展开的循环作为可调优结构。

调优阶段

当可调优结构顺利生成之后,调优阶段便会开始:

  1. Autotuner首先读取生成好的可调优结构的YAML 文件,从而产生对应的搜索空间,也就是生成针对每个可调优代码结构的具体的参数和范围;
  2. 调优阶段会根据设定的搜索算法尝试一组参数的值,生成一个YAML格式的编译配置文件(compilation config),从而让编译器编译目标程序代码产生二进制文件;
  3. 最后Autotuner将编译好的文件以用户定义的方式运行并取得性能信息作为反馈;
  4. 经过一定数量的迭代之后,Autotuner将找出最终的最优配置,生成最优编译配置文件,以YAML的形式储存。

      简单来说,在初始编译阶段,编译器会通过用户指定的调优方向,对可调优的代码区间进行标记。在随后的调优阶段,Autotuner 会根据搜索算法对不同的优化区间生成不同的编译配置。然后使用此配置编译运行,并根据运行性能的反馈来迭代优化配置参数。最后经过给定迭代次数后找出最优配置供用户使用。在实践过程中,通过 Autotuner 对 Coremark Benchmark 进行调优可以获取5%以上的收益。

7、毕昇编译器Fortran前端:特性、架构与优化增强

(1)毕昇编译器Fortran语言前端基于Classic Flang构建,通过增强Classic Flang,支持如下特性:

  • 支持F2003、F2008 (Coarray特性除外)语言标准
  • 最高支持四倍浮点精度(real16)数据类型,15维数组数据类型,支持OpenMP4.0、OpenMP4.5并行化编程模型
  • 支持DWARF4标准
  • 多种Pragma引导语支持,如软件prefetch,omp simd,unroll,vector等

(2)多个Fortran内建函数的优化

  • maxloc/minloc/nint的内联
  • trim/lentrim采用更优算法

(3)内存分配优化,栈内存替挽堆内存

(4)为中后端提供更准确详细的信息来辅助优化

  • 别名分析增强
  • 过程间优化增强

四、总结

  1. 针对不同的场景,不同的应用特点,使用不同的编译优化手段
  2. 编译优化的代价与收益权衡,需要综合考虑性能收益,代码体积,编译时间,可调试性等多方面因素
  3. 软硬件结合,通过软件及硬件的协同优化,最大化的发挥硬件算力
  4. 语言生态构建的持续性,语言标准的不断演进,及新特性支持

【参考】

【1】鲲鹏开发套件:鲲鹏社区官网-凝心聚力 共创行业新价值

【2】鲲鹏首页:鲲鹏社区官网-凝心聚力 共创行业新价值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HwJack20

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值