轮廓引导优化(PGO)–引擎盖下

自我介绍我是 安基特阿斯塔纳 我是后端C++编译器的程序管理器。在我的 最后几个博客 我提供了一个PGO是什么样的介绍以及案例研究,其中包括如何 轮廓引导优化(PGO) 用于生成实际应用程序,例如 SAP NetWeaver Windows PHP更快 . 在这个博客中,我想谈谈PGO是如何在后台工作并帮助生成更快的代码的。让我们看看PGO如何帮助编译器后端构建应用程序的优化版本。一旦我们知道PGO优化是如何工作的,我们就可以理解PGO是如何使应用程序更快的。我们开始吧!

null

继续从我的 第一 博客让我们再来看看以下代码片段:

图片[1]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库

回想一下,PGO通过利用从运行以性能为中心的用户场景收集的概要文件数据来帮助优化应用程序。将应用程序升级需要三个基本步骤 (仪表、培训和优化) . 插装应用程序意味着使用特殊的编译器/链接标志构建,这些标志将探测插入生成的代码中。然后,当在训练阶段被击中时,这些探测器会收集数据,提供在训练阶段使用哪个分支的信息 哪支树枝折断了 代码片段或 *p在非虚拟化中的应用 以代码段为例。探测器在训练阶段收集的这些数据被转储到数据库文件中 *pgd公司 然后作为PGO优化阶段的输入提供给编译器。

在PGO的优化阶段,数据库文件中收集的数据被用作优化列表的输入(下面的表1)。尽管数据库文件被用作许多优化的输入,但是基于内联和布局的PGO优化提供了大部分性能提升,所以让我们看看收集的训练数据如何帮助更好地进行基于内联和布局的决策。

完全和部分内联

功能布局

速度和大小决定

基本块布局

代码分离

虚拟呼叫扩展

交换机扩展

数据分离

循环展开

表1: 从PGO得到的一些优化

PGO的内联决策基于调用图路径分析。简单地说,使用调用图路径评测进行内联决策的基本原则是理解从特定调用路径调用的函数的行为。这一点很重要,因为来自一个调用路径的函数调用的行为可能与另一个调用路径的行为截然不同。有关于哪个调用路径更热的信息有助于更好的内联决策,因为优化器只内联频繁的调用路径,因此最小化了由于内联而导致的代码膨胀,但仍然通过内联更热的调用路径来获得性能。请看下面的示例:

图片[2]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库 图1a:从’goo’、’foo’和’bat’调用函数’bar’的顺序

上面图1a所示的示例说明了从函数“goo”、“foo”和“bat”对函数“bar”进行的函数调用。边上的数字表示函数“bar”分别从函数“goo”、“foo”和“bat”调用的次数。因此,从函数“goo”到函数“bar”的边表示函数“bar”在给定的PGO培训课程中被从函数“goo”调用了10次。现在,调用图路径评测就是从特定的调用路径中找出函数调用的行为。所以图1a将进一步分解为图1b。

图片[3]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库 图1b:从“goo”、“foo”和“bat”调用函数“bar”的顺序 从“bar”调用函数“baz”的序列

通过分析图1b可以明显看出,将函数“bar”内联到“bat”中的主要好处在于,从“bat”到“bar”的调用频率较高,即100次。此外,鉴于从调用路径“goo bar”和“foo bar”调用函数“baz”的频率很高,将“baz”内联到“bar”中的另一个主要优势也有点明显。上述场景中pgo内联的影响如图1c所示。

. 图片[4]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库 图1c:PGO内联的影响,’bar’内联到’bat’,’baz’内联到’bar’。

内联决策是在布局、速度和大小决策以及所有其他优化之前做出的。现在从我的 最后一篇博客 ,让我们回顾一下优化的PGO构建的输出(图2)。

图片[5]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库

“速度与大小”的决定基于后内联动态指令计数。具有较高动态指令计数的代码段(即函数)针对速度进行了优化,而具有较低动态指令计数的代码段针对大小进行了优化。在上面图2所示的构建输出中 3619个评测函数中的6个(0.17%)将被编译以提高速度,其余的函数将被编译以提高大小 是这个决定的结果吗 国王进程。速度和大小决定之后是 ‘块布局’ ‘活代码和死代码分离’ 优化。对基本块进行排序,以便最频繁的路径通过(图3)。为了最小化工作集和提高代码局部性,实现了活代码和死代码的分离。场景失效的代码(函数/块)(即在训练场景中未执行)被放置在一个特殊的部分(图4)。

图片[6]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库

图片[7]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库

图片[8]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库

最后,基于post内联和post代码分离,进行了调用图概要数据函数的布局。只列出带电部分的功能。不包括死块。函数布局背后的总体策略是将强连接的函数(以高频率相互调用)放在一起。如果被叫方位于同一页中,则调用应该具有页面位置。请看图中的示例 下文第6条:

图片[9]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库 图6: 基于调用图和配置文件数据的函数布局

基于训练数据执行的一些其他优化是交换机案例扩展和虚拟呼叫推测。switch case扩展优化使用PGO收集的switch表达式的最常用值,并将其从switch构造中拉出。看看在执行switch case扩展优化时,如何使用PGO数据优化本博客开头的代码片段。

图片[10]-轮廓引导优化(PGO)–引擎盖下-yiteyi-C++库

类似地,如果一个虚拟呼叫, 或者通过函数指针进行其他调用,经常以某个函数为目标,profile-guided优化可以将一个有条件执行的直接调用插入到经常以某个函数为目标的函数中,直接调用可以内联。

尽管Profile-Guided Optimization(PGO)是一项复杂的技术,但这个博客应该为你们提供一个Profile-Guided Optimization的有用性的想法,以及PGO如何在引擎盖下工作以使大量的产品更具性能。在我未来的博客中,我将尝试介绍一个最佳实践指南,它涵盖了常见的陷阱,并为PGO用户强调了一些技巧和窍门。所以请继续关注!另外,如果你想让我们在博客上讨论一些其他与PGO相关的场景,请让我们知道。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享