编译器注释指示如何生成编译器优化代码。为了将编译器注释行同索引行及初始源代码行区分开,以蓝色显示编译器注释行。编译器的各个部分可将注释合并到可执行文件中。每个注释都与源代码的特定行相关联。当写入带注释的源代码后,任何源代码行的编译器注释会直接显示在源代码行的前面。
编译器注释描述为了优化源代码而对其进行的大量转换。这些转换包括循环优化、并行化、内联和流水线作业。以下显示了一个编译器注释的示例。
Function freegraph inlined from source file ptraliasstr.c into the code for the following line 0. 0. 47. freegraph(); 48. } 0. 0. 49. for (j=0;j<ITER;j++) { Function initgraph inlined from source file ptraliasstr.c into the code for the following line 0. 0. 50. initgraph(rows); Function setvalsmod inlined from source file ptraliasstr.c into the code for the following line Loop below fissioned into 2 loops Loop below fused with loop on line 51 Loop below had iterations peeled off for better unrolling and/or parallelization Loop below scheduled with steady-state cycle count = 3 Loop below unrolled 8 times Loop below has 0 loads, 3 stores, 3 prefetches, 0 FPadds, 0 FPmuls, and FPdivs per iteration 51. setvalsmod(); |
请注意,第 51 行的注释包括循环注释,因为函数 setvalsmod() 包含循环代码,并且函数已内联。
可以使用“设置数据表示”对话框中的“源码/反汇编”标签来设置在“源”标签中显示的编译器注释类型;有关详细信息,请参见设置数据表示选项。
一种最常见的优化是可以识别在多个位置出现的同一个表达式,并可通过在一个位置生成该表达式的代码来提高性能。例如,如果在一个代码块的 if 和 else 分支同时出现了相同的操作,则编译器可以仅将该操作移到 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) 0. 0. 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 软件执行概述。