4.2.优化级别

编译器可以执行多种优化以提高执行速度并缩减 C 和 C++ 程序的大小。表 4.2 列出了可用的优化级别、每个级别的范围以及在每个级别执行的一些优化示例。

表 4.2 优化级别
优化级别 范围 执行的优化
--opt_level=off,-Ooff 无。这是 C28x 编译器的默认设置。
--opt_level=0,-O0 语句
--opt_level=1,-O1
  • 执行所有 –opt_level=0 (-O0) 优化,外加:
  • 执行局部常量传播和折叠,复制传播
  • 消除局部公用子表达式
--opt_level=2,-O2 功能
  • 执行所有 –opt_level=1 (-O1) 优化,外加:
  • 循环优化,循环展开
  • 消除全局公用子表达式
  • 消除全局未使用的赋值
  • 生成自动增量寻址
--opt_level=3,-O3 文件(即文件中的跨函数)
  • 执行所有 –opt_level=2 (-O2) 优化,外加:
  • 内联小函数
  • 删除文件内未调用的函数
--opt_level=4,-O4 编程 链接时优化。请参阅 TMS320C28x 优化 C/C++ 编译器用户指南中的第 3.6 节“链接时优化(–opt_level=4 选项)”。

注释

为了生成高效的代码,强烈建议将优化级别设置为 -O2 或更高。

有关这些优化的说明,请参阅 TMS320C28x 优化 C/C++ 编译器用户指南中的第 3.16 节“正在执行什么样的优化?”

4.2.1.示例

4.2.1.1.表达式简化

列表 4.4 说明表达式简化的示例
int32_ttest(int32_ta,int32_tb,int32_tc,int32_td){int32_ttmp;if(d>0)tmp=(a*b)+(a*c);elsetmp=(a*b);returntmp;}

列表 4.4 中的源代码中有 3 个 32 位乘法,需要 IMPYL 指令。在 -O2 处,编译器能够简化表达式以生成 1IMPYL 指令,而不进行优化则为 3 条。

优化级别 所生成汇编代码中的 IMPYL 数
-Ooff 3
-O0、-O1 2
-O2 1

4.2.1.2.常量传播和折叠

列表 4.5 说明常量传播和折叠的示例
1 2 3 4 5 6 7 8 9 10 11 12 13
int32_tconstant(int32_tc,int32_td){int32_ta=42;int32_tb=10;int32_ttmp;if(d>0)tmp=(a*b)+(a*c);elsetmp=(a*b);returntmp;}

这种优化将常量的值传播到表达式中并预先计算常量表达式的结果。

-O2 及更高级别,编译器将表达式替换为:

(d>0L)? (tmp=(c+10L)*42L): (tmp=420L);

即,它将 ab 的值传播到第 8、10 行的表达式中,并在第 10 行计算 a*b,将表达式替换为常数 420

4.2.1.3.删除未使用的赋值

列表 4.6 说明删除未使用的赋值的示例
1 2 3 4 5 6 7 8 9 10 11
int32_tunused_asg(int32_ta,int32_tb,int32_tc,int32_td){int32_ttmp=42;if(d>0)tmp=(a*b)+(a*c);elsetmp=(a*b);returntmp;}

列表 4.6 中,不需要在第 3 行对 tmp 赋值,因为随后分别在第 6 行和第 8 行的 if 和 else 路径上对 tmp 赋值。在 -O0 及更高级别,编译器会删除赋值。

这提高了性能,因为删除了不需要保证正确性的表达式,从而减少了循环数。

4.2.1.4.自动增量寻址

列表 4.7 说明自动增量寻址的示例
int32_taddressing(int32_t*array,int16_tN){int32_tsum=0;int32_ti=0;_nassert(N>0);for(i=0;i<N;i++)sum+=array[i];returnsum;}

-O2 及更高级别,编译器会为列表 4.7 中的循环生成高效的自动递增寻址模式,从而减少执行循环的指令:12 条指令(在 -O1)与 8 条(在 -O2)。

表 4.3 在各种优化级别为循环生成汇编代码
-O1 -O2生成高效的 *XARn++寻址
||$C$L7||: ;*** g2: ;*** sum += array[i]; ;*** if ( (++i) < (long)N ) goto g2; MOVL ACC,XAR5 LSL ACC,1 ADDL ACC,XAR4 MOVL XAR6,ACC ADDB XAR5,#1 MOVL ACC,P ADDL ACC,*+XAR6[0] MOVL P,ACC MOV AL,AR7 MOV ACC,AL CMPL ACC,XAR5 B ||$C$L7||,GT
||$C$L7||: ;*** g2: ;*** sum += *U$7++; ;*** if ( (--L$1) != (-1L) ) goto g2; MOVL ACC,XAR6 SUBB XAR5,#1 ADDL ACC,*XAR4++ MOVL XAR6,ACC MOVB ACC,#0 SUBB ACC,#1 CMPL ACC,XAR5 B ||$C$L7||,NEQ

4.2.1.5.消除无效代码

列表 4.8 说明消除无效代码的示例
int32_tdce(int32_ta,int32_tb,int32_tc,int32_td){int32_ttmp1=a*b*c*d;int32_ttmp;if(d>0)tmp=(a*b)+(a*c);elsetmp=(a*b);returntmp;}

列表 4.8 中,计算出并分配给 tmp1 的表达式是无效的,因为 tmp1 未用在函数中的任何位置。消除无效代码是一种用于删除未使用表达式的编译器技术。在 -Ooff 时,生成的汇编代码包含 6IMPYL 指令,对应于源代码中的每个乘数。在 -O0 时,编译器能够优化代码,并使用无效代码消除和表达式简化的组合将生成的 IMPYL 数量减少到 2 个。

(d>0L)? (tmp=(b+c)*a): (tmp=a*b);

4.2.2.代码大小与速度权衡

有关代码大小与速度权衡的详细信息,请参阅 TMS320C28x 优化 C/C++ 编译器用户指南中的第 3.2 节“控制代码大小与速度”。

4.2.3.优化级别和调试

在更高级别的优化中,调试(例如单步)应用程序变得越来越困难。这是因为在更高的优化级别,编译器会对应用程序进行转换以减少其执行时间、存储器容量占用、功耗或这些的组合。这些转换显著改变了代码的布局,使调试器难以或无法识别与一组汇编指令相对应的源代码。

理想方法是在禁用优化的情况下执行初始开发和调试,然后启用优化。有关详细信息,请参阅启用调试

4.2.4.优化器 interlist

优化会使正常的源代码 interlist 处理变得不现实,因为编译器会大幅度重新排列程序。

--src_interlist 选项会将编译器注释与汇编源语句交叉列出。在启用优化的情况下使用此选项时,interlist 功能不会作为单独的通道运行。相反,编译器会在代码中插入注释,指示编译器如何重新排列并优化了代码。这些注释在汇编语言文件中显示为以 ;** 开头的注释。

表 4.4 --src_interlist 选项的输出
C 源代码 汇编文件中的 Interlist 输出
floatfmac(float*farray,intN){inti;floatsum=0.0f;#pragma MUST_ITERATE(4, , 4)#pragma UNROLL(2)for(i=1;i<N;i++)sum+=farray[i]*farray[i-1];returnsum;}
||fmac||: ;*** ----------------------- U$13 = farray; ;*** ----------------------- L$1 = (N>>1)-1; ;*** 31 ----------------------- sum = 0.0F; ;*** ----------------------- #pragma MUST_ITERATE(2, 16382, 2) ;*** ----------------------- #pragma UNROLL(1L) ;*** ----------------------- // LOOP BELOW UNROLLED BY FACTOR(2) ;*** ----------------------- #pragma LOOP_FLAGS(4103u) ;*** -----------------------g2: ;*** 36 ----------------------- C$1 = U$13[1]; ;*** 36 ----------------------- sum += *U$13++*C$1; ;*** 36 ----------------------- sum += U$13[1]*C$1; ;*** 35 ----------------------- ++U$13; ;*** 35 ----------------------- if ( (--L$1) != (-1) ) goto g2; ;*** 38 ----------------------- return sum;

表 4.4 中的列表可以清楚地看出,优化器已将循环展开 2 倍。来自源代码的原始 pragma 也已更新以考虑展开。有关循环展开的详细信息,请参阅循环展开

警告

--c_src_interlist 选项可能会对性能和代码大小产生负面影响,因为它可以防止某些优化跨越 C/C++ 语句边界。因此,启用优化时建议使用 --src_interlist。在 CCS 中,--src_interlist 选项位于“Build”->“C2000 Compiler”->“Advanced Options”->“Assember Options”下的“Source interlist”下拉列表中。

有关 interlist 选项的详细信息,请参阅 TMS320C28x 优化 C/C++ 编译器用户指南中的第 3.10 节“将 Interlist 功能与优化一同使用”。