马蓉有视频吗:从cpu pipeline阻塞中分析

来源:百度文库 编辑:九乡新闻网 时间:2024/05/03 13:24:31

性能分析--- 从cpu pipeline阻塞中分析

(2009-04-01 16:24:11)转载 标签:

杂谈

http://sh-neo.spaces.live.com/blog/cns!1E3CA285E5F9E122!706.entry ---- pfmon 编译安装http://sh-neo.spaces.live.com/blog/cns!1E3CA285E5F9E122!712.entry .....pmu eventhttp://sh-neo.spaces.live.com/blog/cns!1E3CA285E5F9E122!713.entry .....intel vtune guide 

       pipeline 可以想象为指令或数据的流水线.intel cpu的pipeline一般由两个非同步前后端部分构成.前端从指令高速缓存中收集指令流并将其格式化以备处理器后端处理.它们会暂时存储在中间的指令缓冲器中,以支持异步运行.后端负责数据、指令和功能单元的同步以及在任何时候按照要求停止指令执行.
       当所需指令、数据或计算资源因任何原因无法使用时,pipeline将阻塞并等待一切就绪.仅当后端耗尽了指令缓冲区,最终必须等待前端提供更多指令来继续执行的时候,前端才会发生阻塞.才会对性能产生影响因此最大限度的降低pipeline阻塞通常是软件优化的主要目标.
       被称作周期计数(cycleaccounting)的性能监控工具(基于pmu的)在性能优化过程中意义重大.该性能监控单元(PMU)计算性能监控事件的发生次数.如事件“CPU_CYCLES”可以理解为在程序执行过程中消耗了多少CPU周期.
        同样pipeline的阻塞也能被计算,并能分为结构独立、不相重叠的部分.对于不同的部分,需要以不同的机制来确定内核pipeline阻塞的根本原因并加以排除,借助这种基于周期计数的优化方法,我们在考虑如何提升既定算法的性能时,可以避免许多凭空猜测.
       若想通过优化程序带来重大的性能提升,那么“重要模块”消耗的周期数应至少占 cpu 周期总数的50%.正如阿姆达尔法则(Amdahl'slaw)所指出,在被优化代码区花费的总时间限制了整个程序中性能的上升空间(阿姆达尔法则”:在计算机编程的并行处理程序中,少数必需顺序执行的指令是影响性能的一个要素,即使增加新的处理器也不能改善运行速度.)因此我们要先找hotpots
       其中可能包括带有大量阻塞,如能使这些hotpots中阻塞大幅度减少,便可实现显著的性能提升.
 
 pipeline后端因任何原因阻塞(因而不执行指令)的周期总数由事件BACK_END_BUBBLE.ALL 累积.
       该事件可分解为五个(有时是六个)部分.由于给定的阻塞被分配给唯一源,因此该分解相当精确.这是由于源被区分了优先级,基本分解是:
BACK_END_BUBBLE.ALL = BE_FLUSH_BUBBLE.ALL + BE_L1D_FPU_BUBBLE.ALL +BE_EXE_BUBBLE.ALL + BE_RSE_BUBBLE.ALL + BACK_END_BUBBLE.FE
造成这五个部分的原因是:
1)BE_FLUSH_BUBBLE.ALL:由错误预测的分支或异常情况引起的pipeline清空,进而导致的阻塞.注意这不包括由异常处理器消耗的周期,因为当异常处理代码段运行时,pipeline并不阻塞.
2) BE_L1D_FPU_BUBBLE.ALL:无论对于由 L1数据高速缓存微pipeline或是浮点单元微pipeline引起的阻塞,都需要pipeline都等待.
3) BE_EXE_BUBBLE.ALL:EXE阶段引起的阻塞.这大部分是由于尚未提供所需的输入数据,因而功能单元必须等待.这可能是由于内存子系统延迟或被链式使用的功能单元长时间延迟.
4) BE_RSE_BUBBLE.ALL:寄存器堆栈引擎(Register StackEngine)运行所需的阻塞.如果新的寄存器系列必须分配为局部使用(通常在函数调用堆栈系列中),且有少量寄存器仍为闲置状态,那么之前的寄存器(来自调用堆栈中更高处)必须被移至后备存储以释放所需的寄存器空间.(可以将其想象为切换)与此类似,当调用堆栈被重组时,必须恢复移至后备存储的寄存器.这个进程自动进行,此时,pipeline将阻塞.
5) BACK_END_BUBBLE.FE:这是 BACK_END_BUBBLE.ALL的一个“子事件.它累积pipeline后端因缺少运行指令而阻塞的周期.这通常是由于分支误预测或异常状况需要无法快速提供的指令.
       优先次序的排列是基于情况被检测到时在pipeline中的位置(上面1优先级最高).如果pipeline将被清空(由于分支误预测),那么同时发生的、由于执行单元等待有效数据传输引起的阻塞则与此无关:要求数据的指令处在错误的代码路径中,将不被执行.
       由于事件 BE_FLUSH_BUBBLE.ALL正好可被分割为两个排列了优先次序的子组件,因此可进行继续分解:BE_FLUSH_BUBBLE.ALL =BE_FLUSH_BUBBLE.XPN + BE_FLUSH_BUBBLE.BRU

        由分支误预测和异常情况处理(be_flush_bubble)引起的阻塞以及由缺少由前端(back_end_bubble.fe)传输的指令引起的阻塞往往存在一定关联.缺少指令造成的阻塞通常由误预测或未预测(异常情况)分支引起,而正确预测分支使得硬件在足够提前的时间内预取所需的指令以避免前端阻塞.这种关联可转化为用于提升性能的通用策略.编译器需要更改二进制布局以使预测正确或使所需代码片在内存中处于邻近位置,从而使它们的访问能更好的利用处理器的高速缓存结构.由于热代码片段将被集中来使用更少的ITLB 条目,这会影响指令高速缓存未命中以及 ITLB资源的更有效利用.这种优化编译分两步.编译器建立一个植入的的二进制程序,在处理执行和其它事务期间收集有关分支、内存使用和代码流的数据,然后,当开发人员在典型数据集上运行该植入的的二进制程序时便会创建一个数据文件,其中包括有关分支、内存使用、条件分配结果以及该植入文件为下一步提供的所有信息.接着编译器就会在第二次编译时读取这个数据文件,并将数据用以更改编译器采用的二进制布局和优化策略.
典型范例为 if-else 代码段.所有编译器均假定代码用以下形式编写:

If (dominant flow condition)
     {execute default code}
else(less likely flow)
     {execute more rareinstructions}
       这导致编译器组织代码,因而此故障状态(即没有分支被执行)将调用“default”代码.“rare”代码的执行需要一个分支到达此代码,且另一个分支返回到主代码流.如果此状态并非控制执行流,则档案导引反馈会自动引发二进制布局,如同else if逻辑被颠倒,而“default”和“rare”流被交换.这会导致分支误预测的减少,但更重要的是代码段置于内存,以使经过指令高速缓存的流更为通畅,并减少了由于缺少为后端消耗(即back_end_bubble.fe)准备的指令所引起的阻塞.

        由寄存器堆栈引擎引起的阻塞根源在于编译器调度算法消耗了过多的寄存器资源.这种罕见情况会造成很大影响,不过很容易解决.RSE阻塞通常与错综复杂的环路、递归算法或频繁移动的复杂调用堆栈链有关.OS 可加重对程序吞吐率的影响,这是因为 RSE 引擎的调用能导致OS 切换应用,进而便增加了所耗费的系统时间.因此,以比其它类型后端阻塞 RSE 阻塞相当重要.开发人员为避免 RSE阻塞而采取的措施要视具体情形而定
       有较长复杂环路的程序(意味着诸多指令在环路而非行程数中)可能需要大量寄存器来包含所有中间结果和数据地址.将环路拆为数个较简单环路,并/或使用数据块减少带宽压力,这样可支持编译器使用更少的寄存器来调度计算.由此便无需为获得大量分配数量将寄存器从调用堆栈更上端推移至备用存储.
       对此的一项策略是降低调用堆栈路径中的低 CPU周期使用率程序的优化级别.高度优化通常占用更多寄存器,占据了换取速度的标准空间.因此,对调用堆栈和初始化程序中的中间程序的低级别优化可使得cpu 密集型内核中有更大数量的寄存器可用,让寄存器能发挥其真正用途.
       导引优化(PGO)和过程间优化(IPO)也会有所帮助.由于传输自变量不需要输入/输出寄存器且局部寄存器可重复使用,因此内联函数将减少寄存器的使用.同时,这也可招来相反的结果,当内联代码耗尽资源时,母函数就会变得复杂,此状况应视作编译器错误.

       在多数应用中,最主要的后端pipeline阻塞根源通常与内存访问有关.这种阻塞通常占据了由事件 BE_EXE_BUBBLE 和BE_L1D_FPU_BUBBLE收集的周期的绝大多数.这些事件可分成数个子事件,但用户应清楚子事件并未被赋予优先级因而可能被重计数.这些结果基本上始终能很好地处理.
       BE_EXE_BUBBLE 可分解为两大主导子组件,be_exe_bubble.grall 和be_exe_bubble.frall.它们是分别由访问整数与浮点数据引起的阻塞.be_exe_bubble.grall 可进一步同be_exe_bubble.grgr 事件分解,be_exe_bubble.grgr事件是因数据访问延迟而引起的阻塞,数据访问与长时延迟整数指令(变量转换、多媒体指令、等等,并非数据加载)相关,这些指令与不足的插入指令相连接来吸收这些指令的3 周期延迟.这种情况极为少见.
       

       BE_L1D_FPU_BUBBLE 监控包括与pipeline后端连结的 L1数据高速缓存微pipeline和浮点单元微pipeline的阻塞.两种微pipeline阻塞所响应的情况大不相同.因此开发人员也必须灵活应对.
       如果某些浮点异常状况被测出,浮点单元的微pipeline可能阻塞.其中非规范浮点值的使用和生成是造成异常的最主要原因.反过来,这往往与单精度浮点计算也有关系.借助编译器,开发人员可通过“清零(flushto zero)”编译(-ftz)或将使用的数据类型精度更改为 double 或 real*8 等来抑制非规范性.
       更为复杂的是 L1 数据高速缓存微pipeline阻塞,它由 L1 或 L2 数据高速缓存之数据传输路径的各种拥堵引起,包括各种充满DTLB 的队列,它们需要通过更新来提供从虚拟到物理地址的转换.要查明真正原因,需要对大量子事件进行监控.
       为了确定数据访问阻塞的性质,使其正常工作,通常需要使用更多传统架构事件(2级高速缓存未命中等)来解析执行无效的真正原因.为此,对发生这类事件造成的性能代价进行估测十分关键.多数情况下,可用这些值来估计影响,但也不应盲目行事,因为处理器可并行执行多项处理,所以可能会过高估计影响.