Pogo aka PGO aka Profile-Guided优化
我的名字叫Lawrence Joel,是一位软件开发工程师,在测试C/C++后端编译器组工作。 今天的主题我想博客的一个相当酷的编译器优化,称为配置文件导向优化(PGO或PGO,因为我们在C/C++团队想调用它)。 该工具可用于微软Visual C/C++ 2005和UP。 在这个博客中,我将介绍什么是PGO,它将如何改进您的应用程序以及如何使用它。
什么是PGO?
PGO是一种优化方法,其中编译器使用概要信息为程序做出更好的优化决策。 评测是收集程序在运行时如何使用的信息的过程。 简而言之,PGO是基于用户场景的优化,而静态优化依赖于源文件结构。
PGO采用三阶段方法。 第一阶段可以称为仪器阶段(见图1)。 在仪器阶段, 链接器获取cil文件(这些文件由前端编译器生成,带有/GL标志,例如。 Cl.exe foo.cpp/GL文件 )并将模块传递给C/C++后端编译器。 后端编译器将在需要时插入探测指令。 将使用可执行文件创建.pgd文件;这是一个将在以后阶段使用的数据库文件。 请注意,可执行文件由于探测而膨胀。
第二阶段可以称为训练阶段。 这是在不同场景下运行可执行文件的地方。 探测器将记录运行时信息并将数据保存到.pgc文件中。 每次跑步后 应用程序名称 ! # .pgc文件将被创建(其中appname是正在运行的应用程序的名称,#是1+ 应用程序名称 ! # .pgc文件)。 例如,在图2中,对于可执行文件的每个场景运行,方法调用信息被收集并记录在.pgc文件中。 注意:为了有效地使用PGO,您应该确保您的场景对您的应用程序有良好的覆盖。
第三阶段可以称为PG优化阶段(见图3)。 使用这个阶段,将.pGC文件合并到.pgd文件,该文件将被C/C++后端编译器使用,以对代码做出更好的优化决策,从而使执行更有效。
Pogo有什么优势?
Pogo优化程序中最常接触的区域。 编译器对应用程序的公共输入和控制流有更好的了解。 以下是PGO提供的部分优化列表:
· 内联–通过用每次执行的调用数来衡量方法调用,编译器可以做出更好的内联决策。
· 虚拟调用推测——如果一个特定的派生类型经常被传递到一个方法中,那么它的重写方法可以内联。 这有助于限制对vtable的调用数量。
· 基本块重新排序–此优化会找到执行次数最多的路径,并将这些路径的基本块在空间上放得更近。 这有助于通过优化指令缓存使用率和分支预测实现局部性。 此外,在训练阶段没有使用的代码也会移到最下面的部分。 与下面描述的“函数布局”结合使用可以显著减少大型应用程序的工作集(一个时间间隔内使用的页面数)。
· 大小/速度优化-通过配置文件信息,编译器可以找出函数使用的频率。 利用这些信息,编译器可以优化使用频率较高的函数的速度,并优化使用频率较低的函数的大小。
· 功能布局–如果功能主要基于概要场景一起使用,则将其放置在相同的部分中。
· 条件分支优化–if/else块就是一个例子。 如果条件经常是false而不是true,那么最好在If块之前有else块。
如何使用PGO?
以下是PGO标准用法的步骤;
1. 编译要用flag/GL分析的源代码文件。
2. 用/LTCG:PGINSTRUMENT(或/LTCG:PGI)链接所有文件 . 这将用可执行文件创建一个.PGD文件。注意,当您与/LTCG:PGI链接时,一些优化可能会被覆盖,以便为插装让路。 如果您指定/Ob、/Os或/Ot,这样的优化是有效的。
3. 通过在不同的场景中运行应用程序来培训应用程序 .
4. 用/LTCG:PGOPTIMIZE重新链接文件 (或/LTCG:PGO)生成应用程序的优化映像。
您可能会发现自己在.PGD文件创建后更新了源文件。 如果要用/LTCG:PGO重新链接对象文件,那么配置文件信息将被忽略。 如果您对源文件做了一些小的更改,那么在创建新的.PGD文件和.pgc文件时重复此过程的成本会太高。 要解决此问题,可以改为使用/LTCG:PGUPDATE(或/LTCG:PGU)重新链接文件。 此标志将允许链接使用原始的.PGD文件编译新的源代码。
PGO的另一个有用部分是管理.pgc文件的能力。 visualstudios提供了一个名为Pgomgr的工具,它允许您在经过培训的场景上设置优先级。例如,一家ATM软件公司注意到,在其软件上执行的最常见的交易是取款和存款。 最好将这种事务设置为比在其软件上进行的其他事务更高的优先级。 这可以通过运行以下命令来完成: pgomgr公司/merge:2 应用程序名!1.pgc应用程序名.pgd ,这将给出appname!1.pgc重量为2。 .pgc文件的默认权重为1。 当文件重新链接到/ltcg:pgo or /ltcg:pgu then 应用程序名!1.pgc将比其他.pgc方案具有更高的优先级。
如果您只想在执行或时间间隔内收集概要信息,那么有几种方法可以实现。 有一个名为Pgosweep的工具可以中断正在运行的程序,并将当前配置文件信息存储到新的.pgc文件中,并从运行时数据结构中清除信息。 例如,如果您有一个未结束的应用程序,并且希望区分其白天行为和夜间行为,则可以执行以下操作: pgosweep app.exe白天.pgc . 您可以使用的另一种方法是名为PgoAutoSweep的helper方法。 PgoAutoSweep将有助于在执行中对概要文件信息进行分区。 下面的例子取自Visual C++ 2008中的MSDN的演练,“演练:使用概要引导优化”(当前链接: http://msdn.microsoft.com/en-us/library/xct6db7f.aspx ). 下面的示例将创建两个.PGC文件。 第一个包含在count等于3之前描述运行时行为的数据,第二个包含在此点之后直到应用程序终止为止收集的数据。
#包括 #包括 #包括
int计数=10; int g=0;
void函数2(void) { printf(“来自func2%%d的您好“,计数); 睡眠(2000年); }
void func1(无效) { printf(“来自func1%%d的您好“,计数); 睡眠(2000年); } 空干管(void) { while(计数-) { 如果(g) 函数2(); 其他的 func1(); 如果(计数==3) { PgoAutoSweep(“func1”); g=1; } } PgoAutoSweep(“func2”); } |
注意:为了构建这个示例,我必须编写 cl应用程序cpp/GL “%%VSPATH%%VClibpgobootrun.lib” ,其中%%VSPATH%%是指向最新Microsoft Visual Studio程序目录的路径。
有关PGO的更多信息,请阅读MSDN的非托管C++文章中的Kang Su优秀文章,标题为“微软Visual C++ 2005的概要引导优化”。 当前链接: http://msdn.microsoft.com/en-us/library/aa289170.aspx . 有关PGO使用的信息,您可以查看MSDN的C/C++构建工具部分“配置文件引导优化”页。当前链接: http://msdn.microsoft.com/en-us/library/e7k32f4k.aspx .