Oracle Solaris Studio 12.2:性能分析器

解释带注释的反汇编代码

解释带注释的反汇编代码并不是一项简单的工作。叶 PC 是要执行的下一条指令的地址,因此归属到某个指令的度量应该视为等待执行该指令所用的时间。不过,指令的执行并不总是按顺序发生,而且在记录调用栈时可能存在延迟。要利用带注释的反汇编代码,应该熟悉记录实验的硬件以及装入和执行指令的方式。

接下来的几个小节将讨论有关解释带注释的反汇编代码的一些问题。

指令发送分组

指令按组装入和发送(称为指令发送组)。组中包括哪些指令取决于硬件、指令类型、已执行的指令以及与其他指令或寄存器的各种相关性。因此,可能不会单独发送某些指令,因为它们始终与前一条指令在同一个时钟周期内发送,所以它们从不单独表示下一条要执行的指令。而且,在记录调用栈时,可能有多条指令被视为是下一条要执行的指令。

指令发送规则因处理器类型的不同而不同,并且还取决于高速缓存行内的指令对齐。由于链接程序比高速缓存行要求执行更精确的指令对齐,因此在看上去不相关的函数中进行的更改可能会导致指令的对齐方式不同。不同的对齐方式会引起性能的改进或降级。

下面列举了一种假设情况,即同一个函数在略微不同的环境下进行编译和链接的情况。以下两个输出示例均为 er_print 实用程序的带注释的反汇编代码列表。两个示例中的指令是相同的,但指令的对齐方式不同。

在以下示例中,指令的对齐方式是将 cmpbl,a 两条指令映射到不同的高速缓存行,并且大量的时间都用于等待执行这两条指令。


   Excl.     Incl.
User CPU  User CPU
    sec.      sec.
                             1. static int
                             2. ifunc()
                             3. {
                             4.     int i;
                             5.
                             6.     for (i=0; i<10000; i++)
                                <function: ifunc>
   0.010     0.010              [ 6]    1066c:  clr         %o0
   0.        0.                 [ 6]    10670:  sethi       %hi(0x2400), %o5
   0.        0.                 [ 6]    10674:  inc         784, %o5
                             7.         i++;
   0.        0.                 [ 7]    10678:  inc         2, %o0
## 1.360     1.360              [ 7]    1067c:  cmp         %o0, %o5
## 1.510     1.510              [ 7]    10680:  bl,a        0x1067c
   0.        0.                 [ 7]    10684:  inc         2, %o0
   0.        0.                 [ 7]    10688:  retl
   0.        0.                 [ 7]    1068c:  nop
                             8.     return i;
                             9. }

在以下示例中,指令的对齐方式是将 cmpbl,a 两条指令映射到相同的高速缓存行,并且大量的时间都用于等待执行其中一条指令。


   Excl.     Incl.
User CPU  User CPU
    sec.      sec.
                             1. static int
                             2. ifunc()
                             3. {
                             4.     int i;
                             5.
                             6.     for (i=0; i<10000; i++)
                                <function: ifunc>
   0.        0.                 [ 6]    10684:  clr         %o0
   0.        0.                 [ 6]    10688:  sethi       %hi(0x2400), %o5
   0.        0.                 [ 6]    1068c:  inc         784, %o5
                             7.         i++;
   0.        0.                 [ 7]    10690:  inc         2, %o0
## 1.440     1.440              [ 7]    10694:  cmp         %o0, %o5
   0.        0.                 [ 7]    10698:  bl,a        0x10694
   0.        0.                 [ 7]    1069c:  inc         2, %o0
   0.        0.                 [ 7]    106a0:  retl
   0.        0.                 [ 7]    106a4:  nop
                             8.     return i;
                             9. }

指令发送延迟

有时,会更频繁地出现特定的叶 PC,因为这些 PC 表示的指令在发送前被延迟。导致出现这种情况的原因有多种,下面列出了其中的一些原因:

硬件计数器溢出的归属

除 UltraSPARC 平台上的 TLB 未命中外,硬件计数器溢出事件的调用栈按指令的顺序记录在后续的某些点上,而不是记录在发生溢出的点上,原因之一是处理由溢出产生的中断所用的时间。对于某些计数器(如发送的周期或指令),这种延迟无关紧要。对于其他计数器(例如,那些计数高速缓存未命中或浮点运算的计数器),度量被归属到导致溢出的指令之外的其他指令。通常,引起事件的 PC 只是记录的 PC 前的几条指令,而这些指令可以在反汇编代码列表中正确定位。但是,如果该指令范围内存在分支目标,那么要分辨哪条指令对应于引起事件的 PC 是很困难的,甚至是不可能的。对于计数内存访问事件的硬件计数器,如果该计数器名称带有加号 + 前缀,则收集器会搜索引起事件的 PC。以此方式记录的数据支持数据空间分析。有关更多信息,请参见数据空间分析-h counter_definition_1...[, counter_definition_n]