LIPO-基于编译器技术自定义反馈的轻量级IPO

LIPO-基于编译器技术自定义反馈的轻量级IPO
概述和动机
提高应用程序性能的两种最重要的编译器技术是CMO/IPO(跨模块优化)和FDO(反馈定向优化)。IPO已被证明是提高应用程序性能的一种非常有效的优化技术。在IPO中的所有代码转换中,跨模块内联(CMI)是最重要的转换之一。CMI消除了在单个模块编译中不可能存在的过程边界。由于消除了调用开销、增加了上下文敏感性、更大的优化区域和更大的调度区域,过程边界消除(内联)可以提高程序性能。增加的上下文敏感性允许更多的常亮传播、冗余消除、死代码和不可达代码消除。CMI也有启用程序转换,如间接调用提升。
其余的好处主要来自整个程序分析,如指针分析、mod/ref分析等。然而,IPO确实带来了成本增加的编译时间和大的磁盘存储需求(用于保存源的中间表示)。编译时间的增加主要是由于IPA分析算法的复杂性、IPA中缺乏并行性以及IO活动的增加。概要文件反馈指导编译器做出更好的内联决策和循环展开决策。它还通过函数拆分/概述、块重新排序、函数重新排序来帮助指令缓存性能。它还允许更好的块排序/跟踪以改进分支预测。还可以指导寄存器分配器在溢出候选者选择中做出更好的决定。许多基于价值分析的优化也成为可能,包括间接调用提升、memOp专业化、div/rem专业化。
对于那些想要使用FDO获得CMO的好处,但又不想为增加的编译时间的开销买单的用户来说,编译器最好提供一种轻量级机制来实现这一点。提出了一个轻量级IPO(LIPO),这是一个新的编译模型,以无缝的方式将这两种技术结合在一起。它将重要的IPA分析(如内联分析)从编译时转移到训练运行时,并将包括模块分组信息在内的动态IPA分析结果保存到程序数据库中。通过在运行时执行动态IPA的结果,FDO编译的概要文件使用过程因此可以执行跨模块优化。LIPO代表了程序编制的一个重大范式转变——它使IPO可以通过FDO进行。这种新的编译模型是透明的,不需要用户更改其构建系统(如果构建系统已经包含FDO,这当然是正确的。当LIPO与采样的FDO合并时,这将更加正确,而不需要额外的检测步骤)。它是可扩展的,并且不会丢失构建时的并行性。
基本设计
对于传统的IPO(即链接时间优化LTO),跨模块内联分析被延迟到链接时间,因为它是整个编译中所有模块(翻译单元)可用的第一个点。这需要对程序的所有源模块的中间表示进行序列化。
当使用FDO时,会引入其他步骤。程序构建包括以下步骤:
1)使用指令插入构建目标程序
2)用代表性的输入数据集运行所述仪器化程序;
3)将程序配置文件转储到配置文件数据库中;
4)利用在训练过程中收集到的个人资料反馈建立计划。
FDO的附加步骤通常会给用户带来麻烦,但它提供了一种在不使用LTO的情况下执行传统跨模块分析的方法。
在配置文件集合运行时执行跨模块分析(IPA)的基本思想。在退出正常执行时,程序可以执行额外的分析,而不是简单地将配置文件数据转储到配置文件数据库中。例如,跨模块内联分析。它可以建立动态调用图,用调用计数注释动态调用图边缘,并用与热调用边缘连接的调用者和被调用者对文件模块进行分组。然后将组信息(例如,函数的封装翻译单元的源位置)存储到简档数据库中。
在概要文件使用编译期间,模块分组信息在解析开始之前被读入,对于给定的主源,CMI所需的辅助源模块也被读入和解析。辅助函数参与内联转换,之后删除不需要的辅助模块函数以减少后端编译时间。每个原始源模块都有一个模块组,称为该组的主源。每个模块组包含零个或多个辅助模块。从概念上讲,通过LIPO编译,辅助模块作为头被包括在主模块中,其函数被定义为外部内联。基于跨模块分析,编译器自动、智能地完成了头的包含。
有了这种设计,就不需要对源模块的持久中间表示进行序列化。带有FDO的轻量级IPO的示意图如图1所示。在图中,可以看到IPA分析(模块/功能划分)阶段被合并到配置文件收集运行阶段。所得到的模块分组信息将在概要文件使用编译阶段使用,以指导编译。
  
一个小的例子
该小程序包含3个源模块。模块b.c包含对模块a.c中定义的热函数的调用。模块c.c包含对交流中定义的功能的冷调用。
a.c
 
int foo (int *ap, int i)
{
return ap[i] + ap[i + 1];
}
b.c
 
#include <stdio.h>
#include <stdlib.h>
extern int foo (int *, int);
extern int bar (void);
 
int main (int argc, char **argv)
{
int n, i, s = 0;
int *data;
FILE *data_file;
 
n = atoi (argv[1]);
data = (int *) malloc (sizeof (int) * n);
if ((data_file = fopen ("data.dat","r")) == 0)
return -1;
fread (data, sizeof (int), n, data_file);
 
for (i = 0; i < n - 1; i++)
s += foo (data, i);
 
s += bar ();
fprintf (stderr, "result = %d\n", s);
return 0;
}
c.c
 
extern int foo (int *, int);
int a[4] = {3,4,6,10};
int bar (void)
{
return foo (&a[0], 0) + foo (&a[2], 0);
}
 
posted @   吴建明wujianming  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2023-03-11 消息报道何恺明被曝回归学界
2022-03-11 EDA电子设计技术与应用
2021-03-11 将代码生成器带入TVM
点击右上角即可分享
微信分享提示