du_pl">
<d iv id ="article_content" class="article_content clearfix">
<d iv id ="content_views" class="markd own_views prism-atom-one-light">
display: none;">
d" d ="M5,0 0,2.5 5,5z" id ="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">
原文
三个月前,我使用de>LDC de>按配置文件优化 de>(PGO) de>测量了D编译器de>前端 de>代码,有de>7% de>的性能提升.现在,部分de>PGO de>工作于de>2016 de>年de>1 de>月20日合并到de>LDC de>主分支中!
de>LDC de>使用de>PGO de>,类似de>Clang de>使用de>PGO de>这里,大部分内容也适合de>Clang de>.
d ="PGO_6">按配置优化 de>(PGO) de>
d ="PGO_7">带de>PGO de>构建
de>配置 de>文件可用de>不同 de>的方式获得,但(目前)de>LDC de>仅支持指令分析.编译器在de>第一个 de>编译阶段中添加"de>指令 de>"代码.然后,使用de>要优化 de>代码的de>用例 de>的de>输入 de>,运行程序,并在de>退出 de>时,de>输出 de>配置文件数据到de>单独 de>文件中.编译器在de>第二个 de>编译阶段使用此配置文件数据文件来de>编译和优化 de>.
1,使用指令编译:de>ld c2 -fprofile-instr-generate=profile.raw yourprogram.d -of=instrumented _program de> 2,用常见de>工作 de>负载(要优化 的de>工作 de>负载类型)运行de>instrumented _program de>.这应该创建de>profile.raw de>文件. 3,de>转换 de>原始配置文件为de>LDC de>可用格式:de>ld c-profd ata merge -output=profile.d ata profile.raw de>
4,使用de>配置 de>文件数据de>重新编译 de>程序:de>ld c2 -O3 -fprofile-instr-use=profile.d ata yourprogram.d -of=optimized _program de>
de>指令 de>会使de>程序 de>明显变慢,但de>不必 de>经常生成新的de>配置文件 de>.在de>重新 de>编译有de>更改 de>程序时,可de>重用 de>配置文件数据;de>LDC de>会de>检测 de>控制流的de>变化 de>,并de>简单 de>忽略de>过时 de>的配置文件数据.
d ="_19">配置文件输出文件名
可在de>编译时 de>指定,配置文件输出文件名,如上所示(de>-fprofile-instr-generate=<filename> de>).如果未指定文件名,则改用de>LLVM_PROFILE_FILE de>环境变量.如果未定义de>LLVM_PROFILE_FILE de>,则使用默认de>d efault.profraw de>文件名.
一般,单次运行程序不能创建有de>统计 de>代表的配置文件.想使用不同的de>输入 de>来分析de>多趟 de>指令程序.每次运行程序,可设置de>LLVM_PROFILE_FILE de>为不同文件名,但配置文件运行时还支持配置文件名中的de>替换 de>说明符来帮助你.分别用进程de>pid de>和de>主机名 de>替换文件名中的de>%p de>和de>%h de>说明符.(de>%h de>需要de>LLVM>=3.9 de>).
如果使用de>-fprofile-instr-generate=test.%p.raw de>编译并de>运行 de>几次程序,最终会得到一组de>test.*.raw de>配置文件数据文件.然后,可如常使用de>ld c-profd ata de>合并它们:
de class="prism language-cpp">profd ata merge - o test. profd ata test. * . raw
de>
d ="_30">检查配置文件数据
de>ld c-profd ata de>工具(与构建de>ld c2 de>时使用的de>LLVM de>版本匹配的de>llvm-profd ata de>的重命名版本)可来de>检查 de>配置文件de>数据 de>文件的内容.de>看看 de>如下:
de class="prism language-cpp">d">extern ( C)
d">void foo ( d">int x) {
d">if ( x > 0 ) {
d">if ( x > 500 ) {
}
}
}
d">void main ( ) {
foreach ( i; 0 . . 1000 ) {
foo ( i) ;
}
}
de>
使用
de class="prism language-cpp">ld c2 - fprofile- instr- generate= d">d efault . profraw - run test_foo. d
de>
编译并运行它.然后,
de class="prism language-cpp">ld c- profd ata show - all- functions d">d efault . profraw
de>
输出:
de class="prism language-cpp">Counters:
foo:
Hash: 0x00000000000002cb
Counters: 3
Function count: 1000
_Dmain:
Hash: 0x0000000000000004
Counters: 2
Function count: 1
Functions shown: 2
Total functions: 2
Maximum function count: 1000
Maximum internal block count: 1000
de>
de>配置 de>文件数据文件包含有de>每个函数 de>计数器和de>其他信息 de>的数据结构.在此,看到程序只有de>两个 de>指令函数:de>foo de>和de>_Dmain de>.
de>foo de>函数包含3个执行计数器(每个de>if de>语句,函数项加上计数器),该函数被调用de>1000 de>次(“de>函数计数 de>”).在de>-fprofile-instr-use de>编译步骤中,de>LDC de>使用de>"hash" de>字段来检查是否可使用de>配置文件数据 de>.
d ="_78">重用配置文件数据
de>LDC de>可de>重用 de>旧版本程序获得的de>配置 de>文件数据.在源码稍有de>更改 de>时允许,de>重用 de>配置文件,在配置文件中de>存储 de>函数控制流的de>哈希 de>.在de>-fprofile-instr-use de>编译步骤中,de>LDC de>确保配置文件中的de>函数哈希 de>等于正在编译代码的哈希.
如果没有de>匹配项 de>,de>LDC de>忽略该de>函数 de>的配置文件数据.de>哈希 de>并不完美:不同de>代码段 de>可能产生相同的哈希,因此对de>新代码 de>可应用de>旧版本 de>代码获得的de>配置文件 de>.如:
de class="prism language-cpp">d">void foo ( d">int x) {
d">if ( x > 0 ) {
}
}
de>
de>foo de>函数几乎总是用de>正x de>调用,de>if de>语句的配置文件数据是"是分支有de>1000 de>次,否分支有4次".如果代码现在从de>x>0 de>更改为de>x<0 de>,则de>函数 de>哈希不变(当前de>LDC de>行为),且错误地de>使用 de>了旧的de>配置文件数据 de>.
de class="prism language-cpp">d">void foo ( d">int x) {
d">if ( x < 0 ) {
}
}
de>
de>哈希 de>可依赖更多de>代码 de>来区分de>>和< de>式.目前,哈希主要是为了避免在de>配置 de>文件信息de>不足或过多 de>时出现编译错误,
如,只有de>if de>语句的函数才de>放入 de>哈希中.de>哈希 de>不完善是de>设计 de>使然,因为某些de>更改 de>不太可能de>更改 de>旧配置文件数据的de>有效性 de>.考虑将代码从de>x>1000 de>更改为de>x>1001 de>.此时,de>旧的 de>配置文件数据可能仍足够好,de>丢弃它 de>,性能会更糟.
无需苦恼:de>PGO de>不会de>改变 de>D的de>语义 de>,它只是个de>优化 技术 de>.因此,如果de>配置 de>文件数据不再与de>修改 de>后的de>代码匹配 de>,则可能的最坏情况是程序性能de>下降 de>.因为你已在(de>正确 de>)测量有和没有de>PGO de>的程序性能,所以可轻松知道de>配置 de>文件何时需要更新.
d ="_106">启动代码后重置配置文件
de>ld c.profile de>模块包含对接de>配置 de>文件指令的de>函数 de>.唯一真正有用的函数是de>resetAll() de>.其他de>ld c.profile de>函数公开的de>函数 de>是非常低级的,且在de>编译器/LLVM de>未来版本中可能不可用.de>resetAll() de>重置所有de>性能分析 de>信息,并可从de>配置 de>文件中de>删除 de>程序启动行为:
de class="prism language-cpp">d">import dule">ld c. profile : resetAll ;
d">void main ( )
{
initializeProgram ( ) ;
resetAll ( ) ;
operate ( ) ;
}
de>
这样,de>PGO de>优化 器,为了获得最佳de>操作() de>性能,会降低de>initializeProgram() de>的性能.
d ="_121">允许/禁止函数级分析
在de>函数级 de>分析实现,因此用新的de>(LDC_profile_instr,{true|false}) de>指示de>打开/关闭 de>指定函数的de>指令 de>非常简单.de>指示 de>与de>指示(inline,...) de>有相同的语义.
de class="prism language-cpp">d">void instrumented ( ) { }
d">void not_instrumented ( ) {
pragma ( LDC_profile_instr, false ) ;
}
pragma ( LDC_profile_instr, false ) {
d">void not_instrumented _2 ( ) { }
d">void instrumented 2_overrid e ( ) {
pragma ( LDC_profile_instr, true ) ;
}
pragma ( LDC_profile_instr, true ) :
d">void instrumented 3 ( ) { }
d">void instrumented 4 ( ) { }
}
pragma ( LDC_profile_instr, false )
d">struct Strukt {
d">void not_instrumented ( ) { }
d">void instrumented _method ( ) {
pragma ( LDC_profile_instr, true ) ;
}
}
de>
指令de>默认 de>为de>打开 de>状态.因此,对非常de>可选 de>指令,需要在de>每个 de>文件的开头放de>pragma(LDC_profile_instr,false): de>. 但即便如此,也会指令de>导入 de>函数.如果有de>足够 de>的需求,则很容易实现de>默认关闭 de>指令的de>命令行开关 de>,因此仅de>指令 de>一组de>指定 de>函数就不会那么麻烦了.
d iv>
d iv>
<d iv id ="treeSkill">d iv>
<d iv id ="blogExtensionBox" style="wid th:400px;margin:auto;margin-top:12px" class="blog-extension-box">d iv>