通过改善代码布局提高应用程序性能

 
通过改善代码布局提高应用程序性能

 
By Darryl Gove, 6/9/08  
大型应用程序都存在一个特定的问题:它们拥有许多指令,但是处理器无法一次将整个程序载入芯片。因 此,较大的应用程序需要等待处理器从内存中取出指令,这将花费一些运行时间。本文将讨论一些处理器装载指令的技术,这些技术能帮助处理器更加有效地装载指 令,从而缩短从内存获取数据所耗费的时间。

并非所有指令都是平等的

应用程序拥有许多指令,编写代码时需要考虑所有不可预测的事件--甚至是那些极少(或者从来不)发生的事件。结果,大多数应用程序都通过一组指令来 执行此任务,而许多指令却从未使用过。图 1 形象化地展示了这种情况。下图中的绿色矩形框代表整个应用程序,应用程序中有许多例程,每个例程中都有许多指令,经常执行的指令表示为红色,很少执行的指 令表示为蓝色。

Frame1

很少执行的指令会占用内存和缓存空间,甚至会占用片上存储器的空间。例如,一个高速缓存块可能包含“冷”指令和“热”指令。由于“冷”指令会占用一 定的空间,因此应用程序不得不花费更多的缓存去装载这些“冷”指令,由于代码在内存中的布局不同,因此不同功能的代码可能会占用相同的空间。这种情况称作 缓存失效;这将导致每次可用的关键代码都是有限的。

应用程序存在代码布局问题的症状是拥有大量的指令缓存命中失败事件、缓存 TLB 命中失败事件和分支误测事件。所有这些问题都可通过 UltraSPARC-III 派生处理器上的性能计数器来识别。有关 UltraSPARC-III 的详细信息,请参见:使用 UltraSPARC-IIICu 性能计数器提高应用程序性能

接下来,我们将讨论一些改善内存代码布局的技术,但在此之前,务必认识到这种情况并非只出现在指令级。整个子程序经常要么被调用,要么很少被调用。功能相似的代码库可能包含了经常调用的子程序,之所以会存在这样的两个库,可能是有一个库调用了一些几乎不发生的代码。

由于编译器能够改变代码在内存中的布局方式,因此编译器可以更加高效的使用内存,但要做到这一点需要更多的信息。本文的其余部分将介绍三种改进应用程序代码布局的方法。

使用 Mapfile 对例程重排序

改进这种状况的一种方法是使用 Mapfile。Mapflie 是一个实用工具,用于通知连接程序如何布局内存中的例程。要使用 Mapfile 改进代码的布局,需要根据使用频率对例程排序。

图 2 显示了使用 Mapfile 排序对“热”例程和“冷”例程排序的情况。

Frame2

可以手动生成 Mapfile,但较简单的方法是使用性能分析器:

  • 用标记 -xF 编译程序

  • 使用收集典型工作负载运行程序

  • 使用标记 er_print -mapfile 生成 Mapfile;

  • 使用标记 -xF -M 重新编译应用程序

生成应用程序的 Mapfile 之后,相同的 Mapfile 可以在随后的编译中使用,直至应用程序的配置文件、例程被重新命名,或者添加了额外的例程。

$ cc -O -xF -o app *.c
$ collect app < test_data
Creating experiment test.1.er ...
$ er_print -mapfile app app.map test.1.er
$ cc -O -xF -M app.map -o app *.c

表 1:使用性能分析工具创建 Mapfile

使用配置文件反馈改进指令布局

在例程级上,Mapfile 可以出色地将常用例程从不常用例程中分离出来。然而,大量时间都耗费在指令级上,处理器需要跳过大量未执行的代码块。配置文件反馈一种改进此状况的编译器技术。

配置文件反馈能让编译器了解这些代码通常运行的方式,基于这些信息,编译器可以执行以下类型的优化工作:

  • 重新组织代码,将例程中经常执行的代码组织在一起。

  • 经常调用的内联例程,移除调用例程的成本,并且有助于进一步优化内联代码。

配置文件反馈与交叉文件优化(由 -xipo 标记控制)搭档效果最佳。因为这种组合允许编译器看到所有源文件之间的潜在优化。

图 3 显示了配置文件反馈如何将例程中经常执行的代码重新组织在一起

 

Frame3

配置文件反馈的使用相对比较简单:

  • 使用 -xprofile=collect-xipo 编译应用程序

  • 使用一个或多个典型工作负载运行应用程序

  • 使用 -xprofile=use -xipo 重新编译应用程序

注意,附加 -xipo 标记将支持编译器执行跨源文件优化。

$ cc -O -xprofile=collect:app.profile -xipo -o app *.c 
$ app < test_data
$ cc -O -xprofile=use:app.profile -xipo -o app *.c

表 2:使用配置文件反馈优化应用程序

连接时间优化

Mapfile 在例程级运行,而配置文件反馈在例程中运行;同时优化这两者是一个简单的进阶。连接时间优化(也称作后优化)可以实现此目的。

连接时间优化要求编译器已经完成自己的任务,并且代码存在,我们只需对代码适当布局。要对代码适当布局,连接时间优化会对例程排序,以便将“热”例 程集中在一起,(方法类似于 Mapfile),同时对这些例程中的代码布局,以便将“热”指令集中在一起。但是,连接时间可以超出此范围:

  • 由于“热”代码已被确认,因此可以将它们放在一块,然后将所有的“冷”代码放在一块。这能让所有的“热”代码和“冷”代码一起存储在内存中。

  • 它还可以执行深度优化,因为变量和例程的地址可以被精确算出。因此,连接时间优化器可以简化计算变量或例程地址的表达式,这样可以进一步减少指令数。

图 4 显示经过连接时间优化的应用程序。“热”代码集中放在一起,而“冷”代码则保存一个独立的部分中。

Frame4

连接时间优化需要使用配置文件反馈数据,必要的步骤如下:

  • 使用标记 -xprofile=collect-xipo 编译应用程序

  • 使用一个或多个典型工作负荷运行应用程序

  • 使用标记 -xprofile=use-xipo -xlinkopt 重新编译应用程序

$ cc -O -xprofile=collect:app.profile -xipo -o app *.c
$ app < test_data
$ cc -O -xprofile=use:app.profile -xipo -o app *.c -xlinkopt

表 3:结合连接时间优化与配置文件反馈

结束语

在大型应用程序中使用这些技术可以实现显著的性能提升。需要注意,增加编译次数和编译复杂度会产生额外的开销,因此使用这些技术时需在性能与额外开 销之间做出权衡。还需注意,并非所有应用程序都需要优化代码布局,开发版本可以不需要此流程,并且此流程只适用于最终的产品版本。


关于作者
Darryl Gove 在 Sun Microsystems Inc. 担任编译器性能优化工程师,负责分析和优化当前及未来 UltraSPARC 系统上应用程序的性能。Darryl Gove 拥有英国南安普敦大学运筹学专业的理学硕士和博士学位。在加入 Sun 之前,他曾经在英国从事各种软件架构和开发工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值