编译器作为重要的基础软件之一,在各个领域都有着非常广泛的应用。比如在嵌入式领域,如何在有限的空间内丰富应用的功能困扰着众多的工程师,编译器能够辅助大幅度地降低应用的体积,是工程师们不可或缺的好帮手。
再或是HPC(High Performance Computing,高性能计算)领域聚焦于计算密集型的场景,像气象预报、科学研究等,极致化的应用性能是 HPC 领域不断探索的课题之一,编译器作为重要的性能贡献者,在其中发挥着至关重要的作用。
鲲鹏 GCC 是搭载在鲲鹏平台上的高性能编译器,致力于为用户提供高效的性能体验,在编译算法、指令流水、运算库等方面进行了深度的优化。
更丰富的编译算法:提供丰富的优化算法,如内存布局优化、结构体拆分优化、自动矢量化等,大幅提升指令和数据的吞吐量。 更灵活的指令流水:适配鲲鹏平台的指令流水优化,发挥鲲鹏架构极致算力。 更高效的运算库:深度优化数学库算法,提供丰富的矢量数学函数接口,大幅提升数学计算的效率。 鲲鹏 GCC 编译器特性架构图 当前鲲鹏 GCC 编译器已广泛应用于多种 HPC 典型场景,如气象、安防、流体力学等,性能优势已经逐步展露。其中,SPECCPU 2006 和 SPECCPU 2017 benchmark 跑分平均优于开源 GCC 15% 以上,HPC 典型气象应用 WRF 优于开源 GCC 10% 以上。 鲲鹏 GCC 编译器与开源编译器性能对比 结构体拆分优化——大幅提升 Cache 命中率 SPECCPU 2006 benchmark 中有一款 libquantum 子项,它用于模拟量子计算机运行整数分解的 Shor 算法,该子项的一个瓶颈在于频繁连续的内存读写,简化后的代码示例如下图左边所示。 内存布局优化原理示意图 从循环中可以看出在结构体 node_t 中,data1 的使用率极高,而 data2 是不使用的。在源代码中,数据是以结构体数组的形式排布在内存中,按照普通编译器的编译方式,每次从内存中取数据时会将连续几个结构体数据加载到 cache 中,而 cache 包含的数据中有一半将不会参与运算,这会造成 cache 空间和带宽的浪费与性能的损耗。 鲲鹏 GCC 编译器会自动检查循环中适合优化的场景,通过将结构体拆分成两个结构体的方式(如上图右),将有效的数据紧凑排布起来,从而提高 cache 命中率和应用性能。经测试,此优化可以给 libquantum 子项带来80%的性能提升。除此之外,鲲鹏 GCC 编译器还支持结构体全展开、结构体成员重排列等内存布局优化,让应用程序的性能如虎添翼。 指令流水优化——更加适合鲲鹏的指令调度模型 通用的指令流水调度是基于各个指令的消耗和指令间的依赖关系合理地调度指令执行的顺序。以下图举例,在不进行指令流水优化时,6条指令依次顺序执行,假设每条指令需要两个单位的执行时间来计算得到结果,由于指令间存在数据依赖,在没有计算得到 V1 值的条件下,无法进行 V0 值的计算,由此导致了一个单位时间的等待,造成性能损失。在使用指令流水优化后,在 V 系列指令执行等待的一个单位时间内加入了没有数据依赖的K系列指令的执行,所有指令的执行被合理地调度了起来,指令流水优化提升了指令执行的效率和程序运行的性能。 指令流水优化原理示意图 鲲鹏芯片基于 ARM 架构,对指令的消耗和指令间依赖关系的处理进行了优化和增强。鲲鹏 GCC 编译器导入了基于鲲鹏芯片的指令模型,使得指令流水优化的结果能够更加适合鲲鹏芯片的执行,提升鲲鹏软件运行的性能。经测试,该优化可以给 SPEC CPU 2006benchmark 带来2%的整体性能提升。 高性能运算库——极致性能的数学库和矢量数学函数 HPC 领域会运用到大量的数学函数计算,如 pow、sinf、log 等,也经常需要对整个数组内的数据做数学函数运算。如下图举例所示,左边是需要对 a 数组的所有数据进行 sinf 数学计算。正常情况下会循环遍历 a 数组依次对每个数据进行 sinf 数学计算,无法获得进一步的性能提升空间。鲲鹏 GCC 编译器能够识别该场景,自动将 sinf 数学函数的调用转化为矢量化 vec_sinf 的数学调用,能够同时处理四个数据的 sinf 数学计算,并矢量化存取数据,得到性能的提升。经测试,该优化可以给 HPC 领域 NEMO 应用带来6%的性能提升。