Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1143386
  • 博文数量: 286
  • 博客积分: 3124
  • 博客等级: 中校
  • 技术积分: 5186
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-24 23:42
个人简介

Bomi

文章存档

2015年(1)

2013年(1)

2012年(281)

2008年(3)

分类: 系统运维

2012-02-05 00:42:52

By Darryl Gove、Chris Aoki, 8/18/08

配置文件反馈(Profile  feedback)是一种有用的机制,它可以将代码的运行时信息提供给编译器。这些信息有助于显著提升应用程序的性能。与所有的优化方法一样,只有当配置文件反馈能够提高程序性能时才值得使用。
在为编译器提供训练数据(training  data)时,需要小心选择典型的工作负荷。可以通过比较 tcov 或 Performance Analyzer 等工具收集到的配置文件来检查工作负荷的典型性。
简介在编译应用程序时,编译器将尽量选择最佳的指令和最佳的代码布局。这必须根据源代码做出决策。但源代码不包含与代码动态行为相关的信息,因此编译器必须通过启发式算法提供“最佳猜测”。
启发式算法可用于确定如何构造代码,哪些例程应该内联,哪些代码段需要频繁执行等诸多细节。

示例 1 展示了编译器可能会遇到的一个问题:

void  calculate(...)
            {

if(...some condition...)
            {
                 //do calculation
                  ...
            }

A

else

{
                //do caclulation
                ...
            }

B

//do more  work
            ...

}

代码示例 1

在示例 1 显示的代码中,编译器需要做一项有意义的决策;它可以采用不同的方法来构造代码。编译器应该将 A 或 B  设定为默认方式(这样可使路径更快),还是应该在构造代码时使两个代码分支具有相同的性能呢?
在编译器的众多决策中,合理安排 if 语句只是影响代码布局的决策之一。其他决策包括:

  • 例程执行是否足够频繁,从而能通过内联提高程序性能?
  • 是否能将代码放置在内存中,以便更加有效地使用指令缓存?
  • 是否存在迭代次数足够多且值得打开的循环?
大多数决策只能通过启发式算法管理,因为编译器没有程序的运行时行为信息。但是,借助于运行典型“训练”数据集得到的数据,配置文件反馈机制使编译器能够收集到程序的运行时信息。

在 SPEC CPU2000  基准测试程序中使用配置文件反馈,可以将浮点集性能平均提高 7%,整数集性能提高  16%。对于各部分代码,性能提高情况各不相同,有些代码没有提高,有些则显著提高。

构建配置文件反馈

配置文件反馈的理念是让程序运行一段时间,并收集该程序的运行时信息。然后,编译器通过这些数据精选优化决策。

使用配置文件反馈的流程是:

  1. 使用 -xprofile=collect  标记构建二进制代码。该标记将生成一个特殊版本的应用程序(称为检测码),用于在运行时收集运行数据。
  2. 然后,使用“训练”工作负荷运行应用程序。训练工作负荷可代表应用程序将完成的实际工作,但不需要运行实际工作负荷那么长的时间。
  3. 使用 -xprofile=use  标记重新构建二进制代码。该标记使用早先收集的数据优化二进制代码。

这意味着,使用配置文件反馈之后,构建过程花费的时间是不使用配置文件反馈时的两倍。这是因为构建过程需要两次通过编译器,还需要应用程序运行一小段时间。因此,使用复杂的构建过程换取运行时的性能提升是值得的。

选择典型工作负荷

通过配置文件反馈构建应用程序要求使用典型训练工作负荷将应用程序的运行时行为通知给编译器。工作负荷的要点包括:

  • 运行时间短。不必通过长时间运行来提高发送给编译器的数据的质量。
  • 工作负荷应该测试应用程序的所有重要部分。Sun Studio Performance Analyzer 和 tcov (1)  等工具可用于估算训练工作负荷是否覆盖了代码的所有重要部分,训练工作负荷的特性与实际工作负荷是否相似。
  • 如果可以提高代码的覆盖面,则应使用若干个训练工作负荷。

人们有时会担心使用错误训练工作负荷是否会导致性能变差。这是有可能的,出现这种情况一般有两个原因:

  • 训练工作负荷没有覆盖整个应用程序。问题代码碰巧使用了在编译器中没有足够信息的部分应用程序。
  • 问题工作负荷的行为与训练工作负荷有很大不同。

在这两种情况下,都可以增加另一个训练工作负荷来改进问题工作负荷的性能。也可以通过查看代码覆盖面或各个例程所花的时间来确定性能不同的原因。训练工作负荷造成其他工作负荷运行缓慢的情况是很少见的。更常见的情况是,训练数据显示某项特别的优化是多余的,或者使用附加的训练数据证明有必要进行优化并且会改进问题工作负荷的性能,而不影响其他工作负荷性能。

配置文件反馈的好处

编译器拥有的信息越多,就越有助于应用程序的优化。和所有优化方法一样,有些代码会得到很大获益,而有些则不会。这很大程度上由代码类型决定。

因配置文件反馈而受益的代码类型很可能是带有大量条件语句(if  语句)的代码。那些具有非常强的预测行为、且此行为对编译器来说不明显的代码将会获益最大。

这类代码的一个简单例子是程序中需要检验准确值的地方。编译器不能很容易地确定程序员希望检验通过还是不通过,因此一般会假设通过和不通过机会均等。然而,如果检验的是“合法数据”,大多时候代码中的数值是合法的,配置文件反馈就会使编译器知道这个情况,并且适当地优化代码。

配置文件反馈使程序性能得到提升的另一种情况是当配置文件可用于为编译器选择最佳的内联程序时。内联有两个好处,一个是减少了调用例程的开销,二个是公开了优化的更多机会。内联的缺点是它会导致代码数量增加;如果内联代码没用,那么代码数量的增加可能导致程序性能降低。配置文件反馈帮助编译器正确地选择频繁调用的例程作为侯选,同时拒绝很少调用的例程。

配置文件反馈的编译器标记

- xprofile  标记的作用是通知编译器构建应用程序并收集配置文件,或者构建应用程序并且使用现有的配置文件。在使用标记时,需要注意一些细微之处。

  • -xprofile=collect  带有一个可选参数,告诉编译器配置文件信息放置在哪里。例如:  

    当程序执行时,-xprofile=collect:myapp将配置文件数据放置在当前目录的myapp.profile 目录下。类似地,-xprofile=collect:/tmp/myapp将配置文件数据放置在 /tmp/myapp 目录下。如果没有指定放置地点,就将配置文件数据放置在 .profile目录下, 是正在运行的可执行文件的名字。

  • -xprofile=use  也可以带有一个可选参数,告诉编译器配置文件数据放置在哪里。  

    -xprofile=use:/tmp/myapp会使用放置在 /tmp/myapp.profile 的配置文件数据。如果没有指定放置地址,编译器就会在当前目录下的 a.out.profile中寻找数据。注意这与 -xprofile=collect阶段是不同的行为。不同的原因是当采集数据时,配置文件采集器可以确定可执行文件的名字,而是当编译器使用配置文件数据构建新应用程序时,不知道用于收集配置文件的应用程序的名字。

    注意:在构建可执行程序时,总为配置文件数据指定一个完整的地址是一个很好的习惯。


使用配置文件反馈指定其他编译器标记

使用  -xprofile=collect  收集配置文件数据编译应用程序时,还是用低优化级别生成二进制代码,以便比使用优化二进制代码收集到更加详细的数据。生成的二进制代码有特殊的代码布局,这取决于源代码和构建时使用的标记。如果标记改变,代码布局也会改变。

注意:  除了参数 -xprofiler,最好在收集和和使用两个阶段都使用相同的标记。


运行可执行文件收集配置文件信息

当运行可执行文件时,配置文件数据被写入系统文档中。写入过程发生在运行的最后阶段,因此如果应用程序不能完成运行,那么也许不会有配置文件数据写入。如果应用程序多次运行,那么配置文件数据将会累积所有的运行结果。

如果源代码修改过,最好不要再用修改以前的配置文件数据。使用原来的配置文件数据,可能编译器不会报告错误,但是更可能导致编译器不能采用最优方案。

注意:  每次构建新的 -  xprofile=collect,移除原有的配置文件数据,当源代码改变时,收集的新的配置文件数据,这是一个很好的习惯。


使用由配置文件反馈收集的数据的编译器选项

有几个编译器选项会使用配置文件反馈信息:

  •     当优化级别是 -xO5 时,配置文件反馈使编译器能够在一些代码频繁生成的区域生成推断指令。没有配置文件反馈,当优化级别是 -xO5  时编译器仍然会生成推断指令,但是很少。
  • 编译器标记 - xipo 和 - xcrossfile 执行  crossfile 优化 --  意思是跨越多个源文件的优化。这种优化的一个例子是将一个程序从一个源文件内联入另一个源文件的代码中。有配置文件反馈提供信息,编译器就有了设置内联例程的好模型。

  • 编译器标记 - xlinkopt  使编译器能够执行链接时间优化。编译的最后阶段使用生成代码的所有知识对代码布局做最后调整。这对可以通过代码布局将所有频繁执行的代码放在一起而使性能提升的大型代码很有用。

使用配置文件反馈的示例代码示例 2 中显示的代码可以通过配置文件反馈改善代码布局。从代码中可以明显看出,时间大多花费在调用函数 f上。这个函数将传递进来的六个数值相加,但在执行相加之前,程序将检查数值指针的合法性。在本例中,所有的数值都是合法的,并且对于程序中的这类检查,大多数情况下,数据会是合法的。然而,编译器不能辨认测试结果通常将会是合法的,必须假定,if 语句中的两种情况机会均等。

            #include
#include
static unsigned f( unsigned *a0, unsigned *a1, unsigned *a2,
unsigned *a3, unsigned *a4, unsigned *a5)
{
unsigned result = 0;
if (a0 == NULL) {printf("a0 == NULL");} else {result += (*a0);}
if (a1 == NULL) {printf("a1 == NULL");} else {result += (*a1);}
if (a2 == NULL) {printf("a2 == NULL");} else {result += (*a2);}
if (a3 == NULL) {printf("a3 == NULL");} else {result += (*a3);}
if (a4 == NULL) {printf("a4 == NULL");} else {result += (*a4);}
if (a5 == NULL) {printf("a5 == NULL");} else {result += (*a5);}
return result;
}
void main(int argc,const char *argv[])
{
int i, j, niters = 1, n=6;
unsigned sum, answer = 0, a[6];
niters = 1000000000;
if (argc == 2) { niters = atoi(argv[1]); }
for(j=0; j{
  a[j] = rand();
  answer += a[j];
}
for(i=0; iif (sum == answer) { printf("answer = %u\n", answer); }
else { printf("error sum=%u, answer=%u", sum, answer); }
}           

代码示例 2 - 使用配置文件反馈提升性能演示

示例 3 显示了未使用配置文件反馈编译和运行程序的结果。

            $ cc -O -o example example.c
$ timex example 1000000000
answer = 86902
real 43.87
user 43.28
sys 0.00           

代码示例 3 -不使用配置文件反馈的编译和运行

示例 4 显示了通过配置文件反馈编译该代码的过程。注意,其中某个程序训练运行使用的迭代远少于程序的主循环。

            $ cc -O -xprofile=collect:./example -o example example.c
$ example 100
answer = 86902
$ cc -O -xprofile=use:./example -o example example.c
$ timex example 1000000000
answer = 86902
real 34.52
user 33.93
sys 0.01           

代码示例 4 -使用配置文件反馈的编译和运行

两段代码在运行时间上的 10 秒差距代表了 25%  的性能提升。显然,这个特殊的例子演示了配置文件反馈优化的作用,但是它所揭示的原理也出现在大多数代码中。
关于作者

Darryl Gove 在 Sun Microsystems的编译器性能工程部门担任高级工程师,负责分析和优化现在及未来 UltraSPARC系统中的应用程序的性能。他拥有英国南安普敦大学运筹学专业的硕士及博士学位。在加入 Sun 之前,他曾在英国从事各种软件架构和开发工作。
Chris Aoki 是 Sun SPARC 编译器后台小组的一名工程师。他曾在 Sun  负责几代编译器技术的代码生成和优化工作。目前,他的项目主要涉及为基于反馈的优化提供编译器和运行库支持。
阅读(309) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~