Oracle Solaris Studio 12.2:性能分析器

编译器注释

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

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


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 软件执行概述