TMS320C6678 Programmar Guider
本文是对DSP程序优化的第2到4章的部分翻译,水平有限,不妥之处敬请原谅。
2 优化C/C++代码 2.1
写C/C++代码
本章描述了如何分析和裁剪你的代码,以确认你已经从C6000体系获得最好的性能。
2.1.1 数据类型小诀窍
当写C代码时遵循以下指导方针:
1. 避免编码int和 long类型一样大小,因为C6000编译器的long型为40bit; 2. 尽可能使用short数据类型为fixed-point乘法的输入,因为这种数据类型在
C6000里提供了最有效的16-bit乘法器。
3. 当有循环计数时,使用int或者unsigned int,而不要使用short和unsigned
short,以避免不必要的符号扩展说明。
2.1.2
分析C代码的性能
2.2 编译C/C++代码
编译工具提供了一个shell程序(cl6x),可以用来编译,汇编优化,汇编和单步链接程序。调用编译时输入:
cl6x [options] [filenames] [?z [linker options] [object files]]
2.2.1 编译选项
表2.1 代码关键性能避免选择的编译器选项
选项 -g/-s/ -ss 描述
这些选项限制了优化C语句的数量,导致较大的代码大小和较慢的执行速度 使软件流水线调试无效。用-ms2/-ms3代替来减少代码大小将在其它
代码大小的优化上禁用软件流水
总是使用-o2/-o3来最大化编译分析和优化,在性能和代码大小之间用
-o1/-o0
代码大小标志(-msn)来权衡
过时的。在3.00前,这个选项改善代码,但是在3.00后,这个选项
-mz
会降低性能增加代码大小。 表2.2 性能编译选项 -mu 选项 -mh
描述
允许投机执行。适当的填充量必须在数据内存中可用来确保正确执行,
这一般不是问题,但必须坚持如此。
描述了编译器的中断阈值。如果你知道在你的代码中没有中断发生,
-mi
编译器可以在软件流水循环一个代码大小前后避免启用和禁止中断,
-mii
另外,高寄存器压力循环的充分利用有潜在的性能改进。 -mt§
?o3? ?op2§ ?pm?
使编译器能够使用假设,使编译器能与某些优化更先进。 代表最高水平的优化。各种循环执行优化,比如软件流水,展开,和
SIMD,各种文件级别特征也用于提高性能。
指定模块不包含提供给编译器的外部源码调用或者修改的函数和变
量。改善了变量分析和允许假设。
将源文件联合来执行程序层次的优化。
? Although ?o3 is preferable, at a minimum use the ?o option. ? Use the ?pm option for as much of your program as possible. § These options imply assertions about your application.
表2-3 编译器选项稍微降低性能和改善代码大小 选项 -ms0 -ms1
描述
主要优化性能,其次代码大小。可以用在所有的例程上,但最多用在
性能关键例程里。
禁止所有自动控制大小的内联(-o3允许)。用户指定的内联函数仍然
-oi0
允许。
表2.4中的选项都是控制代码选择的,结果是以较小的性能下降导致较小的代码大小。
表2.4 控制代码的编译器选项 选项
?o3? ?pm?
描述
代表最高水平的优化。各种循环执行优化,比如软件流水,展开,和
SIMD,各种文件级别特征也用于提高性能。
将源文件联合来执行程序层次的优化。
?op2 ?oi0 ?ms2?ms3
指定模块不包含提供给编译器的外部源码调用或者修改的函数和变
量。改善了变量分析和允许假设。
禁止所有自动控制大小的内联(-o3允许)。用户指定的内敛函数仍然
允许。
主要优化代码大小,其次性能。
表2-5描述的选项为信息,不影响性能和代码大小。
表2-5 信息的编译器选项 选项 -mw -k -s/-ss
描述
用这个选项来产生额外的编译器回馈,这个选项对性能和代码大小没
影响。
保持汇编文件,这样你就可以检查和分析编译器反馈。
在汇编中交叉C源码和优化评论。-s选项显示小性能下降;-ss选项显
示更严重的性能下降。
2.2.2 内存依赖(Memory Dependencies)
为了使你的代码最大有效化,C6000编译器安排了尽可能多的指令并行。为了安排并行指令,编译器在指令间必须决定依赖关系。依赖意味着一个指令必须在另一个指令之前发生;比如,一个变量在被调用前必须从内存中被加载。因此只有独立的指令才能并行执行,所以依赖性抑制并行性。
? 如果编译器不能决定两个指令是否存在依赖,那么它假设一个依赖项,
安排这两个指令循环计算完成第一个指令所需要的任何延迟。 ? 如果编译器可以决定这两个指令是独立于另一个的,那么可以安排并行。 编译器决定指令访问内存是否有依赖性是很难的,下面的技术帮助编译器决定哪个指令是没有依赖性的:
? 用restrict关键词指出一个指针是唯一的可以指向一个特定的对象的指
针声明的范围的指针。
? 用-pm选项,这个选项赋予编译器对所有的程序和模块全局访问和在排
除依赖性上允许它更激进。
? 用-mt选项,允许编译器使用假设消失依赖性。
2.3 2.4
Refining C/C++ Code Refining C/C++ Code
表2-10 Memory Access Intrisics
Type C compiler Intrisic _memd8(p)
Description
开始于地址p(存在内联)的非对齐访问double
型数据 开始于地址p的非对齐访问常量double型数据
开始于地址p的double型数据对齐访问 开始于地址p的常double型数据对齐访问 开始于地址p的unsigned int型数据的非对齐访
问
开始于地址p的常unsigned int型数据的非对齐
访问 开始于地址p的unsigned int型数据的对齐访问
_memd8_const(p) Double
load/store
_amemd8(p)
_amemd8_const(p)
_mem4(p)
Unsigned int load/store
_mem4_const(p) _amem4(p) _amem4_const(p)
开始于地址p的常unsigned int型数据的对齐访
问
开始于地址p的unsigned short型数据的非对齐
_mem2(p)
访问
开始于地址p的常unsigned short型数据的非对
Unsigned _mem2_const(p)
齐访问
short
开始于地址p的unsigned short型数据的对齐访
load/store _amem2(p)
问
开始于地址p的常unsigned short型数据的对齐
_amem2_const(p)
访问
指针p可以是任何类型。可是为了允许编译器正确的辨认指针别名,这些内联函数的指针参数p要正确的辨认指向对象的类型。比如,如果你想一次获得4个short型数据,那么在_memd8()里的p指向的类型必须是short。
2.4.2.1 Using Word Access in Dot Product(用字访问点积)
点积中用到了_mpy()和_mpyh()内联函数;
C编译器内联
int _mpy(int src1, int src2); int _mpyus(uint src1, int src2); int _mpysu(int src1, uint src2); uint _mpyu(uint src1, uint src2); int _mpyh(int src1, int src2);
Multiplies the 16 MSBs of src1 by the 16
描述
Multiplies the 16 LSBs of src1 by the 16 LSBs
ofsrc2 and returns the result. Values can be signedor unsigned.(src1的低16位跟src2的低十六位相乘,返回结果。返回值可以是signed或unsiganed)
int _mpyhus(uint src1, int src2); int _mpyhsu(int src1, uint src2); uint _mpyhu(uint src1, uint src2); int _(int src1, int src2);
int _mpyhuls(uint src1, int src2); int _mpyhslu(int src1, uint src2); uint _mpyhlu(uint src1, uint src2); int _mpylh(int src1, int src2); int _mpyluhs(uint src1, int src2); int _mpylshu(int src1, uint src2); uint _mpylhu(uint src1, uint src2);
MSBs
of src2 and returns the result. Values can be signed or unsigned.(src1的高十六位和src2的高十六位相乘,返回的值可以是signed或者unsigned)
Multiplies the 16 MSBs of src1 by the 16 LSBs of
src2 and returns the result. Values can be signed
or unsigned.(src1的高十六位和src2的低十六位相乘,返回值可以是signed或unsigned)
Multiplies the 16 LSBs of src1 by the 16 MSBs of
src2 and returns the result. Values can be signed
or unsigned.(src1的低十六位和src2的高十六位相乘,返回值可以使signed或unsigned)
2.4.2.3 Using Doubleword Access for Word Data (C64x/C64x+ and C67x Specific)
本节的Float Dot Product函数中用到了_itof(),_hi(),_lo()内联函数;
C/C++ Compiler
Description
Intrinsic float _itof(uintsrc); Longlong_itoll(uint src2, uint src1) uint _lo(double src);
将src的unsigned的比特数解释成float型。
通过重新诠释来创建一个新的long long型的寄存器组,src2是高(奇数)寄存器和src1为低(偶)寄存器。 从double寄存器组返回低(偶)寄存器
uint _lmbd(uint src1, 搜索由src1的最低位决定的src2的最左边的1或者0,uint src2); 返回的比特数目由改变的比特决定。 double _ltod(long src); 重新定义long 寄存器组为double寄存器组 uint _hi(double src); uint _lo(double src);
返回一个double寄存器组的高(奇)寄存器。 返回一个double寄存器组的低(偶)寄存器