JavaScript is required to for searching.
跳过导航链接
退出打印视图
Oracle Solaris Studio 12.3:性能分析器     Oracle Solaris Studio 12.3 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

1.  性能分析器概述

2.  性能数据

3.  收集性能数据

4.  性能分析器工具

5.  er_print 命令行性能分析工具

6.  了解性能分析器及其数据

7.  了解带注释的源代码和反汇编数据

工具如何查找源代码

带注释的源代码

性能分析器"源"标签布局

标识初始源代码行

"源"标签中的索引行

编译器注释

通用子表达式删除

循环优化

函数内联

并行化

带注释的源代码中的特殊行

源代码行度量

解释源代码行度量

度量格式

带注释的反汇编代码

解释带注释的反汇编代码

指令发送分组

指令发送延迟

硬件计数器溢出的归属

"源"、"反汇编"和 `PC` 标签中的特殊行

外联函数

编译器生成的主体函数

动态编译的函数

Java 本机函数

克隆函数

静态函数

非独占度量

分支目标

存储和装入指令的注释

在不运行实验的情况下查看源代码/反汇编代码

-func

-{source,src} item tag

-{disasm,dis} item tag

-{cc,scc,dcc} com-spec

-outfile filename

-V

8.  操作实验

9.  内核分析

索引

带注释的源代码

可在性能分析器中查看实验的带注释的源代码,方法是选择分析器窗口左窗格中的“源”标签。另外,可以使用 er_src 实用程序,在不运行实验的情况下查看带注释的源代码。本手册的此部分介绍了源代码如何在性能分析器中显示。有关使用 er_src 实用程序查看带注释的源代码的详细信息,请参见在不运行实验的情况下查看源代码/反汇编代码

分析器中的带注释的源代码包含以下信息:

性能分析器“源”标签布局

“源”标签分为若干列,其中左侧固定宽度的几列显示各个度量,右侧的其余部分显示带注释的源代码。

标识初始源代码行

在带注释的源代码中,所有以黑色显示的行都来自于初始源文件。在带注释的源代码列中,每行开头的数字与初始源文件中的行号对应。以不同颜色显示其中字符的行可能是索引行,也可能是编译器注释行。

“源”标签中的索引行

源文件是指可编译生成目标文件或解释为字节代码的任何文件。目标文件通常包含与源代码中的函数、子例程或方法对应的一个或多个可执行代码区域。分析器分析目标文件,标识每个作为函数的可执行区域,并尝试将其在目标代码中找到的函数映射至与目标代码相关联的源文件中的函数、例程、子例程或方法。当分析器操作成功后,它将在带注释的源文件中对应于在目标代码中找到的函数的第一条指令处添加一个索引行。

带注释的源代码会为每个函数(包括内联函数,即使内联函数没有在“函数”标签中的列表中显示)显示一个索引行。在“源”标签中以红色斜体显示索引行,且索引行中的文本括在尖括号中。类型最简单的索引行与函数的缺省上下文对应。任何函数的缺省源上下文都被定义为该函数的第一条指令所归属的源文件。以下示例显示了 C 函数 icputime 的索引行。

                    578. int
                    579. icputime(int k)
0.       0.         580. {
                         <Function: icputime>

从上面的示例能够看出,索引行紧跟在第一条指令行的后面。对于 C 源代码,第一条指令对应于函数体开头的开型花括号处。在 Fortran 源代码中,各个子例程的索引行跟在包含 subroutine 关键字的行后面。同样,main 函数索引行跟在应用程序启动时执行的第一个 Fortran 源代码指令的后面,如以下示例所示:

                                   1. ! Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved.
                                   2. ! @(#)omptest.f 1.11 10/03/24 SMI
                                   3. ! Synthetic f90 program, used for testing openmp directives and the
                                   4. !       analyzer
                                   5. 
0.       0.       0.       0.      6.        program omptest
                                      <Function: MAIN>  
                                   7.
                                   8. !$PRAGMA C (gethrtime, gethrvtime)

有时,分析器可能无法将它在对象代码中找到的函数映射至与该对象代码相关联的源文件中的任何编程指令;例如,可能从另一个文件(如某个头文件)执行代码 #included 或内联操作。

此外,以红色显示的行还有一些特殊索引行,以及其他一些不是编译器注释的特殊行。例如,由于编译器优化的原因,可能会为目标代码中的某个函数创建一个特殊索引行,但该索引行并不对应在任何源文件中编写的代码。有关详细信息,请参阅"源"、"反汇编"和 `PC` 标签中的特殊行

编译器注释

编译器注释指示如何生成编译器优化代码。为了将编译器注释行同索引行及初始源代码行区分开,以蓝色显示编译器注释行。编译器的各个部分可将注释合并到可执行文件中。每个注释都与源代码的特定行相关联。当写入带注释的源代码后,任何源代码行的编译器注释会直接显示在源代码行的前面。

编译器注释描述为了优化源代码而对其进行的大量转换。这些转换包括循环优化、并行化、内联和流水线作业。以下显示了一个编译器注释的示例。

0.       0.       0.       0.        28.       SUBROUTINE dgemv_g2 (transa, m, n, alpha, b, ldb,   &
                                     29.      &                   c, incc, beta, a, inca)
                                     30.       CHARACTER (KIND=1) :: transa
                                     31.       INTEGER   (KIND=4) :: m, n, incc, inca, ldb
                                     32.       REAL      (KIND=8) :: alpha, beta
                                     33.       REAL      (KIND=8) :: a(1:m), b(1:ldb,1:n), c(1:n)
                                     34.       INTEGER            :: i, j
                                     35.       REAL      (KIND=8) :: tmr, wtime, tmrend
                                     36.       COMMON/timer/ tmr
                                     37. 
                             Function wtime_ not inlined because the compiler has not seen 
                             the body of the routine
0.       0.       0.       0.        38.       tmrend = tmr + wtime()


                             Function wtime_ not inlined because the compiler has not seen 
                             the body of the routine
                             Discovered loop below has tag L16
0.       0.       0.       0.        39.       DO WHILE(wtime() < tmrend)
                              
                             Array statement below generated loop L4
0.       0.       0.       0.        40.       a(1:m) = 0.0
                                     41. 
                                    
                             Source loop below has tag L6
0.       0.       0.       0.        42.       DO j = 1, n       ! <=-----\ swapped loop indices
       
                             Source loop below has tag L5
                             L5 cloned for unrolling-epilog.  Clone is L19
                             All 8 copies of L19 are fused together as part of unroll and jam
                             L19 scheduled with steady-state cycle count = 9
                             L19 unrolled 4 times
                             L19 has 9 loads, 1 stores, 8 prefetches, 8 FPadds, 
                             8 FPmuls, and 0 FPdivs per iteration
                             L19 has 0 int-loads, 0 int-stores, 11 alu-ops, 0 muls, 
                             0 int-divs and 0 shifts per iteration
                             L5 scheduled with steady-state cycle count = 2
                             L5 unrolled 4 times
                             L5 has 2 loads, 1 stores, 1 prefetches, 1 FPadds, 1 FPmuls,
                             and 0 FPdivs per iteration
                             L5 has 0 int-loads, 0 int-stores, 4 alu-ops, 0 muls, 
                             0 int-divs and 0 shifts per iteration
0.210    0.210    0.210    0.        43.          DO i = 1, m    
4.003    4.003    4.003    0.050     44.             a(i) = a(i) + b(i,j) * c(j)
0.240    0.240    0.240    0.        45.          END DO  
0.       0.       0.       0.        46.       END DO  
                                     47.       END DO
                                     48. 
0.       0.       0.       0.        49.       RETURN
0.       0.       0.       0.        50.       END

可以使用“设置数据表示”对话框中的“源/反汇编”标签来设置在“源”标签中显示的编译器注释类型;有关详细信息,请参见设置数据表示选项

通用子表达式删除

一种很常见的优化是可以识别在多个位置出现的同一个表达式,并可通过在一个位置生成该表达式的代码来提高性能。例如,如果在一个代码块的 ifelse 分支同时出现了相同的操作,则编译器可以仅将该操作移到 if 语句前。同时,编译器将基于前面某次出现的该表达式为指令分配行号。如果分配给通用代码的行号与 if 结构的一个分支对应,并且该代码实际上始终包含另一个分支,则带注释的源代码会在不包含的分支内的行上显示度量。

循环优化

编译器可以执行多种类型的循环优化。部分比较常用的优化如下:

循环展开是指在循环体中重复多次循环迭代,并相应调整循环索引的过程。随着循环体的增大,编译器能够更加有效地调度指令。同时降低由于循环索引递增和条件检查操作而引起的开销。循环的剩余部分则使用循环剥离进行处理。

循环剥离是指从循环中删除一定数量的循环迭代,并适当将它们移动至循环的前面或后面的过程。

循环交换可更改嵌套循环的顺序,以便最大程度地降低内存跨距 (memory stride),最大程度地提高高速缓存行命中率。

循环合并是指将相邻或位置接近的循环合并为一个循环的过程。循环合并的优点与循环展开类似。此外,如果在两个预优化的循环中访问通用数据,则循环合并可以改进高速缓存定位 (cache locality),从而为编译器提供更多利用指令级并行性的机会。

循环分裂与循环合并正好相反:它将一个循环分裂为两个或更多的循环。如果循环中的计算量过于庞大,导致寄存器溢出进而造成性能降级,则有必要采用这种优化。如果一个循环中包含多个条件语句,也可以使用循环分裂。有时可以将循环分成两类:带条件语句的和不带条件语句的。这可以提高不带条件语句的循环中软件流水线作业的机会。

有时,对于嵌套式循环,编译器会先使用循环分裂来拆分循环,然后执行循环合并,以不同的方式将循环重新组合,以达到提高性能的目的。在这种情况下,您可以看到与以下注释类似的编译器注释:

    Loop below fissioned into 2 loops
    Loop below fused with loop on line 116
    [116]    for (i=0;i<nvtxs;i++) {
函数内联

利用内联函数,编译器可将函数指令直接插在该函数的调用位置处,而不必执行真正的函数调用。这样,与 C/C++ 宏类似,内联函数的指令会在每个调用位置进行复制。编译器在高优化级别(4 和 5)执行显式内联或自动内联。内联可以节约函数调用方面的开销,并提供可以优化寄存器使用和指令调度的更多指令,但代价是代码会占用内存中较多的资源。以下为内联编译器注释的一个示例。

                Function initgraph inlined from source file ptralias.c 
                    into the code for the following line
0.       0.         44.       initgraph(rows);

注 - 在分析器的“源”标签中,编译器注释不会换行,即不会显示在两行上。


并行化

如果您的代码包含 Sun、Cray 或 OpenMP 并行化指令,则可经过编译在多个处理器上并行执行。编译器注释会指示执行过并行化操作和尚未执行并行化操作的位置,以及相应的原因。下面显示了一个并行化计算机注释的示例。

0.       6.324       9. c$omp  parallel do shared(a,b,c,n) private(i,j,k)
                   Loop below parallelized by explicit user directive
                   Loop below interchanged with loop on line 12
0.010    0.010     [10]            do i = 2, n-1

                   Loop below not parallelized because it was nested in a parallel loop
                   Loop below interchanged with loop on line 12
0.170    0.170      11.               do j = 2, i

有关并行执行和编译器生成的主体函数的更多详细信息,请参阅OpenMP 软件执行概述

带注释的源代码中的特殊行

在“源”标签中,还可以看到有关某些特殊情况的另外几种注释,它们或者显示为编译器注释,或者显示为与索引行颜色相同的特殊行。有关详细信息,请参阅"源"、"反汇编"和 `PC` 标签中的特殊行

源代码行度量

每行可执行代码的源代码度量都会在固定宽度的几列中显示出来。这些度量与函数列表中的度量相同。您可以使用 .er.rc 文件更改实验的缺省值;有关详细信息,请参见设置缺省值的命令。您还可以在分析器中使用“设置数据显示”对话框更改显示的度量和突出显示的阈值;有关详细信息,请参见设置数据表示选项

带注释的代码在源代码行级别显示应用程序的度量。该度量是通过使用记录在应用程序调用堆栈中的 PC(program count,程序计数),并将每个 PC 映射至源代码行的方式生成的。要生成带注释的源文件,分析器首先确定在特定目标模块(.o 文件)或装入对象中生成的所有函数,然后从每个函数扫描所有 PC 的数据。为了生成带注释的源代码,分析器必须能够找到并读取目标模块或装入对象来确定从 PC 至源代码行的映射,此外,它还必须能够读取源文件来生成要显示的带注释的副本。有关用于查找实验源代码的过程的说明,请参见工具如何查找源代码

编译过程要经过很多阶段,这取决于请求的优化级别,并且会发生转换,该转换会使指令到源代码行的映射变得混乱。对于某些优化,源代码行信息可能完全丢失,而对于其他优化,源代码行信息则可能变得混乱。编译器依赖各种试探操作来跟踪指令的源代码行,而这些试探操作不是绝对无误的。

解释源代码行度量

指令的度量必须解释为在等待执行指令时累积的度量。如果记录事件时执行的指令来自与叶 PC 相同的源代码行,则度量可以解释为由于执行了该源代码行的缘故。但是,如果叶 PC 与所执行的指令来自不同的源代码行,那么叶 PC 所属源代码行的度量中至少有一些度量必须解释为在等待执行此代码行时累积的度量。例如,当一个源代码行上计算的值被用在下一个源代码行上时。

当执行过程中存在严重的延迟(如高速缓存未命中或资源队列停止)或指令等待前一个指令返回结果时,如何解释度量的问题是最重要的问题。在这些情况下,源代码行的度量看上去很高(高得不太合理),应该查看代码中的其他邻近的行以找出导致高度量值的行。

度量格式

表 7-1中说明了可能出现在带注释的源代码行上的四种度量格式。

表 7-1 带注释的源代码度量

度量
含义
(空白)
程序中没有 PC 对应于该代码行。此情形应该始终适用于注释行,并且在下列情况下适用于出现的代码行:
  • 所出现的代码段的所有指令已在优化期间删除。

  • 代码在其他地方重复,并且编译器执行通用子表达式识别并标记带有其他副本代码行的所有指令。

  • 编译器用不正确的行号来标记该行的指令。

0.
程序中的某些 PC 被标记为从该代码行派生,但没有数据引用这些 PC:它们从不会位于被抽样统计或被跟踪的调用堆栈中。度量不表示该行不执行,只是表示该行不以统计方式显示在分析数据包或记录的跟踪数据包中。
0.000
该行至少有一个 PC 出现在数据中,但是计算的度量值舍入为零。
1.234
归属于该行的所有 PC 的度量总计达到了显示的非零数值。