C66x定点浮点混合DSP循环编程优化指南
C66x DSP是TI最新出的定点和浮点混合DSP,后向兼容C64x+和C67x+、C674x系列DSP。本文介绍了基于C66x架构的常用优化技巧,首先介绍C66x相对于C64x+定点DSP的浮点和定点处理能力的增强,以及C66x新引入的128-bit的数据类型。接下来说明c66x特有的特性和相关的优化技术,重点在其浮点增强以及对复数运算和矩阵、向量运算的intrinsics选择,最后是如何解决寄存器不足、SIMD move的使用平衡寄存器和功能单元的分配以及解决寄存器生命周期过长的问题的高级优化技巧。本文中的编译结果基于CCSv4.1中的CGTools v7.2编译器,编译选项–o3 –s –mw –mv6600。
C66x DSP简介
C66x DSP是TI最新出的定点和浮点混合DSP,后向兼容C64x+和C67x+、C674x系列DSP。最高主频到1.25GHz,RSA指令集扩展。每个核有32KB的L1P和32KB的L1D,512KB到1MB L2存储区,2MB~4MB的多核共享存储区MSM,多核共享存储控制器MSMC能有效的管理核间内存和数据一致性。针对通信应用,其片内集成了2个TCP3d Turbo码字译码器,一个TCP3e Turbo码编码器,2个FFT/IFFT,DFT/IDFT协处理器以及4个VCP2 Viterbi译码器。高速互联总线,4个串行RapidIO接口,千兆网口、EMIF-DDR3内存控制器。TeraNet Switch用于片内和外设间的快速交互。
C66x DSP的架构和指令增强
TMS320C66x ISA架构是对TMS320C674x DSP的增强,也是基于增强VLIW架构的,具有8个功能单元(2个乘法器,6个ALU算术运算单元),该架构的基本增强如下:
4倍的乘累加能力, 每个周期32个 (16x16-bit)或者8个单精度浮点乘法;
浮点运算的增强:优化了将TMS320C67x +和TMS320C64x+ DSP 结合的TMS320C674x DSP,原生支持IEEE 754单精度和双精度浮点运算,包括所有的浮点操作,加减乘除;浮点运算的SIMD支持以及单精度复数乘法,附加的灵活性,如在.L和.S单元完成INT到单精度SP的相互转换
浮点和定点向量处理能力的增强: TMS320C64x+/C674x DSPs支持2-way的16-bit数据SIMD或者4-way的8-bit,C66x增加了SIMD的宽度,增加了128-bit的向量运算。如QMPY32能做2个包含4x32-bit向量的乘法。另外SIMD的处理能力也得到增强;
复数和矩阵运算的引入和增强:针对通信信号处理中的常用复数算术函数和如矩阵运算的线性算法的应用,如单周期可以完成两个[1×2]复数向量和[2×2]的矩阵乘法
图2. QMPY32的向量操作
图2. C64x+/C674x/C66x ISA定点和浮点处理能力对比
128-bit数据类型
__x128_t是一个保存128-bit的数据类型的容器,用于C6600的SIMD处理。当使用__x128_t容器时,需要包含头文件c6x.h并且通过编译选项-mv6600使能针对C6600平台的编译。这个类型可以用于定义针对特定C6600的intrinsics。当使用_x128_t对象时,一个__x128_t占4个寄存器,如果在内存则对齐在128-bit边界。
定义一个__x128_t全局对象,(e.g. __x128_t a;),默认放在.far段。
定义一个__x128_t局部变量,(e.g. __x128_t a;),放在堆栈
定义一个__x128_t global/local指针 (e.g. __x128_t *a;)
定义一个__x128_t数组 (e.g. __x128_t a[10];)
定义一个结构体,联合体、类包含一个__x128_t类型对象
__x128_t对象赋值给另外一个__x128_t对象
传递一个__x128_t对象给函数入口参数,通过值传递
函数返回一个__x128_t对象
使用128-bit处理的intrinsics来设置或者摘取内容。
而像针对__x128_t对象的原生+, -, *操作是不允许的,也不能进行类型转换,也不能把__x128_t对象传递给像printf 的I/O函数,而应该摘取元素输出。
C66x DSP的浮点运算和向量、复数、矩阵运算的优化
本节考虑C66x的一些特殊的地方,如浮点运算和向量、矩阵运算的优化。针对浮点运算,可以考虑如下:什么时候决定采用浮点(高精度,高动态范围),消除因为定点实现引入的缩放和舍入运算,使用浮点独有的求倒数和求平方根的倒数的指令、快速的进行浮点和定点数据类型转换的指令等。而对于向量和复数矩阵运算,则考虑更为有效的复数操作指令,向量和矩阵运算的独特指令。
浮点操作
C66x的浮点支持可以原生的运行很多的浮点算法,即便是从Matlab或者C代码刚刚转换的算法,就可以评估性能和算法精度。本节主要以单精度浮点为例,虽然C66x可以很好的支持双精度浮点的运行。
使用C66x的浮点操作有以下好处,由于不用考虑精度和数据范围权衡而进行的定点数据Q定标和数据转换,因而在通用C和MatlAB上验证的算法可以直接在C66x的DSP上实现。浮点处理还能从减少的缩放和Q值调整带来的cycle减少,浮点操作还提供快速的出发和求平方根的指令,单精度浮点处理能带来很高的动态范围和固定的24-bit精度,和32-bit定点相比更节省功耗。而快速的数据格式转换指令更能有效的处理定点和浮点混合的代码,带来更多的便利性。
C66x的浮点算术运算包括如下:
每个周期内和C64x+核相同数量的单精度浮点操作,即8个,CMPYSP 和 QMPYSP在一个周期能处理4个单精度乘法。每个周期8个定点乘法操作,4倍于C64x+核;
加减操作,每个周期8个单精度加减操作,DADDSP和DSUBSP能处理2个浮点加减,而且可以在.L 和 .S功能单元上执行。
浮点和整型的转换:8个单精度到整数,8个整数到单精度浮点转换,DSPINT, DSPINTH, DINTHSP, 和DSPINTH能转换2个浮点到整型,可以在.L 和 .S功能单元上执行
除法:每个周期2个倒数 1/x 和平方根的倒数1/sqrt(x),为了获取更高的精度可以采用如牛顿-拉夫森等迭代算法。
一下是一个求解复数数据的幅度和相位的函数
- void example1_gc(cplxf_t *a, cplxf_t *ejalpha, float *abs_a, int n)
- {
- int i;
- float a_sqr, oneOverAbs_a;
- for ( i = 0; i < n; i++)
- {
- a_sqr =a<span style="font-style: normal;">.real * a.real + a</span><span style="font-style: normal;">.imag * a</span><span style="font-style: normal;">.imag;
- oneOverAbs_a =1.f/(float)sqrt(a_sqr);
- abs_a</span><span style="font-style: normal;"> = a_sqr * oneOverAbs_a;
- ejalpha</span><span style="font-style: normal;">.real =a</span><span style="font-style: normal;">.real * oneOverAbs_a;
- ejalpha</span><span style="font-style: normal;">.imag =a</span><span style="font-style: normal;">.imag * oneOverAbs_a;
- }
- }</span>
复制代码
输入的测试序列使用rand()函数产生实部和虚部,然后减去0x4000来让随机数据在[-16384 16383]范围内,对于C66x,直接通用C的API。另外本例中采用C64x+的代码实现来做对比。对于定点代码需要把输入数据转换为定点的16-bit的I/Q格式,相位用Q15格式表示。本例中的编译环境CCSv4.1.0,编译器Compiler 7.2.0A10232,使用Nyquist Device Cycle Approximate Simulator, Little Endian的编译和运行方式,对Big Endian也类似。
如果直接用上述的浮点代码进行编译执行,会发现C64x+平台需要473483个周期,而C66x 平台需要 69644个周期,这主要因为对于两个平台而言以上循环都没法进行软件流水因为循环体内有函数调用,对于C64x+而言,不仅1/sqrt(x)函数需要调用math函数,所有的浮点运行也是需要调用定点模拟的函数实现的。对于C66x平台而言,只有1/sqrt(x)函数需要调用math函数,这也就是为什么会有6.8x的性能提升。这个提升也很好的作为验证算法的准确性。
|