原作者: danowens
原帖地址:CoreMark and Compiler Performance
CoreMark正在迅速引起人们关注,成为事实上的 CPU 性能基准测试标准。这是一款易于编译和运行的免费工具,能返回一个有价值结果,简化了性能分析。与上世纪 90 年代的Dhrystone 一样,我们发现开发人员正在试图根据 CPU 性能判断编译器效率。这种判断往往会令人误解,因为CoreMark 与之前的Dhrystone 一样,属于特殊用途的基准测试小工具,它以 CPU 性能而非广泛的嵌入式软件工作负载为目标。CoreMark在判断编译器效率方面的价值取决于应用程序与该基准测试的相似程度。最近有客户索要基于 ARM 编译器编译的CoreMark 性能数据;因此,我们决定研究CoreMark 及它作为编译器效率指标的潜在价值。本篇博文介绍了我们的经验,这些经验在最新版本的 ARM 编译器上带来了CoreMark 性能大幅提升。
背景
过去,基准测试允许比较编译器效率和 CPU 性能。第一款流行基准测试 SIEVE 于 1983 年 1 月在 BYTEMagazine 中发布,它仅计算质数。之后,Dhrystone和Whetstone 流行起来。Dhrystone着眼于整数和字符串运算,而Whetstone 主要使用浮点运算。当今的编译器技术允许在编译时计算许多内部基准测试运算,这些基准测试的 CPU 性能指示可能会令人误解。CoreMark使用随机生成的数据作为输入,使得编译器无法在编译时预先计算一部分基准测试。尽管CoreMark 与Dhrystone 相比更难“被超越”[1],但聪明的编译器作者通过精心制作以基准测试编码结构为目标的优化,依然成功提高了基准测试成绩。从编译器作者的角度来看,这犹如瓮中捉鳖!
在开始实验时,我们首先分析CoreMark 编码结构和该基准测试执行的函数。CoreMark包含一个链表数据结构,该结构在运行时被扫描。接着,控制卸载到由循环控制的状态机或矩阵运算例程,具体取决于链表数据值。在分析之后,我们确定了应对状态机的技巧,并找到了更加激进地展开循环的机会。
状态机分析:CoreMark 状态机实施为封装在循环中的一系列开关语句。下方是此结构的一个示例。
根据开关语句的结构,我们了解了每个条件语句后的新目的地,因而能够将每个分支的条件直接指引到后续条件,从而消除相应的开关。例如:
根据具体的代码,例如封闭循环结构和开关前后的代码数量,这种转换可以提升普通状态机的性能。正如您可能想到的,在每个开关条件后复制开关之前和之后的代码将导致代码长度增加。稍后将详细介绍。
循环展开:ARM 编译器使用试探来确定要展开的循环。展开过于激进可能会导致代码大小大幅膨胀;展开过于保守则可能会牺牲一部分性能。ARM 编译器一般采用保守的循环展开方式,因为我们的客户通常开发内存往往受限的嵌入式应用程序。默认试探考虑了对代码长度的总体影响,试图在提高性能的同时不会让生成的代码大幅膨胀。作为这项实验的一部分,我们采用了更加激进的循环展开方式,通过调整编译器的试探方式,激进地展开循环而不管代码长度增加。
结果:ARM 编译器显示 CoreMark性能大幅提升应用上述技巧后,CoreMark基准测试性能显著提升。虽然结果喜人,但有个不利的副作用,在启用开关语句和激进循环优化后代码长度增加了 13%。由于 ARM 编译器的受众是嵌入式开发人员,这样的结果通常是我们的客户不能接受的。
代码长度为何重要如上文所述,性能改善技巧附带了不受欢迎的代码长度影响。一般而言,ARM 强大的 32 位微控制器拥有充足的计算能力,能够满足嵌入式工作负载所需,但由于成本原因,嵌入式应用中的存储器容量往往有限。尽管 MCU 的价格已大幅下降,但改用片载闪存更大的微控制器可能会大幅增加您的 BOM 成本。例如,购买 1500 片热门 ARM Cortex-M3 微控制器时,128KB MCU 闪存版本的单价为 4.88 美元,而 64KB 闪存的版本仅为 2.80 美元。因此,主要针对性能而调节的编译器可能会导致总体项目成本的大幅增加。
代码长度在当今现代微处理器中之所以如此重要,系统成本并不是唯一的原因。简洁的代码可增加能够载入缓存的指令数量,通过高效使用缓存或可提高性能;或许更重要的是,或可降低总体功耗。
ARM编译器团队始终同时关注性能和代码密度,编译器经过精心调节,在执行速度和代码大小之间取得平衡。为了推崇简洁代码生成,我们开发了MicroLib,这是针对基于 ARM 的嵌入式应用程序而优化大小的代码库。与标准的 C 语言代码库相比,MicroLib在代码长度上有显著的优势。例如,上文所述的代码长度增加 13% 在使用MicroLib 时转变为代码减小 23%。
对于对性能重视程度高于代码长度的用户而言,上述CoreMark 性能提升必定受到欢迎。例如,如果代码中利用开关语句或编写良好的 [2[2] for循环和while 循环实施了有限状态机,那么这些结构的性能会大幅提升。利用我们的标准基准测试套件(由 60 多个以广泛嵌入式使用案例为目标的应用程序组成)测试这些新优化时,仅有一部分出现大幅性能提升。这并不奇怪,因为CoreMark 仅仅是一个小软件,以一组有限代码结构为目标的定向编译器优化可能无法放大到更广泛的代码基础上。
总结通过应用专门针对基准测试代码结构的编译器优化,可以大幅提升CoreMark 性能。通过比较不同编译器的CoreMark 分数,可以发现哪个编译器更善于处理类似CoreMark 的代码,但这不一定能改善实际嵌入式应用程序的性能;而且更糟糕的是,可能会带来不利的代码膨胀。利用MicroLib 等精简代码库,可以缓减代码长度方面的副作用。与往常一样,最佳的做法是根据代码评估感兴趣的编译器,在使用激进性能优化时考虑对代码长度的影响。
如果有兴趣评估本篇博文提到的 ARMCompiler 性能改进,可以下载Keil MDK-ARMv4.70 或ARM DS-5 v5.14(2013 年 3 月提供)。发行说明详细说明了如何使用新的优化。建议可以试用一下。如有反馈,可以随时联系我。
----[1] 上下界为已知常量的惯用循环、上界未知的循环、包含少量 C 语句的循环。
[2]通过在编译时预先计算值或者优化掉代码的定时部分,可以超越Dhrystone。