循序渐进做优化:从C62x到C64x一例(上)

2019-07-29 16:36发布

一、引言 f

TI TMS320C641X DSP是德州仪器最高端的数字信号处理器,性能卓越,但因为其价格相对较高,只能针对特定行业的高端用户,对于大部分DSP研发人员,有时甚至很难接触到它们,所以相关深入的讨论很少。而媒体处理器TI TMS320DM642/DM640/DM643出现了,它集成了丰富的外设,甚至可以说,对于音视频和基于网络的应用比C641X更易使用,价格却十分便宜,所以在各种领域已开始大规模应用,当然关键的仍然是它们有着与C641X一样TI最强大的内核C64x,所以希望高效地(如更高分辨率或更多路)使用DM64x,除了片上内存的有效利用外,程序上基于C64x指令集(VelociTI.2 Core)的调度汇编级的优化变得十分关键。
TMS320C64x指令集与C6000公共指令集(VelociTI,支持流水线、VLIW,如TMS320C62x所使用的)相比共扩展了88条指令,这些指令的扩展是建立在其CPU结构改进基础上的,主要分三种情况:
(1)与C6000公共指令集的指令功能及运行完全一致,只是增加了可执行的功能单元。这类指令共有6条。
(2)与C6000公共指令集内对应指令的功能及运行基本一致,主要差别是指令操作数的类型增加了,即大量引入了SIMD(单一指令多数据流扩展)指令。这类指令有34条。
(3)新增指令48条。
原来基于C62x(VelociTI)的优化程序可以不做更改的运行在C64x,但如果能够利用这些扩展的指令,情况将如何呢?本文将通过实例详细说明如何应用C64x系列指令进行程序优化,以达到提高执行效率的目的。这里我们以mpeg-4编解码中的一个函数为例,具体说明如何应用C64x系列指令集进行调度汇编级的程序优化。
二、应用C6000公共指令集的优化程序

在这里,将要进行优化的C语言函数为:
void interpolate8x8_halfpel_h_c(uint8_t * dst,
const uint8_t* src,
const uint32_t DstStride,
const uint32_t Srcstride,
const uint32_t rounding)
{
uint32_t i, j;
for (j = 0; j < 8; j++) {
for (i = 0; i < 8; i++) {
int32_t tot =(int32_t) src[j * Srcstride + i] + (int32_t) src[j * Srcstride + i + 1];
tot = (tot + 1 - rounding) >> 1;
dst[j * DstStride + i] = (uint8_t) tot;
}    }   }
上面函数的执行体为一嵌套循环,函数的源操作数和目的操作数都是字节型数据。为了对比说明C64x系列指令集的优势,我们首先用C6000系列的公共指令集对该函数的循环体进行汇编优化,其内核程序(因为篇幅原因,流水线填充和排空部分不另外列出,可参考内核导出)如下:
H_LOOP:      ;循环内核
       add    .l1    A_src1,A_src2,A_dst1             ;1
       ||  add    .l2    B_src5,B_src6,B_dst5
       ||  ldbu   .d1t1  *+A_ptrSrc[4],A_src5
       ||  ldbu   .d2t2  *+B_ptrSrc[4],B_src9
       ||  sub    .s1    Acnt,1,Acnt
          add    .s1    A_dst1,A_round,A_dst1            ;2
       ||  add    .s2    B_dst5,B_round,B_dst5
       ||  add    .l1    A_src2,A_src3,A_dst2   
       ||  add    .l2    B_src6,B_src7,B_dst6
       ||  ldbu   .d1t1  *++A_ptrSrc[A_SrcStride],A_src1
       ||  ldbu   .d2t2  *++B_ptrSrc[B_SrcStride],B_src5
          shru   .s1    A_dst1,1,A_dst1                   ;3
       ||  shru   .s2    B_dst5,1,B_dst5
       ||  add    .l1    A_dst2,A_round,A_dst2
       ||  add    .l2    B_dst6,B_round,B_dst6
       ||  ldbu   .d1t1  *+A_ptrSrc[1],A_src2
       ||  ldbu   .d2t2  *+B_ptrSrc[1],B_src6   
          add    .l1    A_src3,A_src4,A_dst3              ;4
       ||  add    .l2    B_src7,B_src8,B_dst7

       ||  shru   .s1    A_dst2,1,A_dst2

       ||  shru   .s2    B_dst6,1,B_dst6

       ||  stb    .d1t1  A_dst1,*A_ptrDst++[1]

       ||  stb    .d2t2  B_dst5,*B_ptrDst++[1]

          [A1]B   .s2    H_LOOP                        ;5

       ||  add    .l1    A_dst3,A_round,A_dst3

       ||  add    .l2    B_dst7,B_round,B_dst7

       ||  stb    .d1t1  A_dst2,*A_ptrDst++[1]

       ||  stb    .d2t2  B_dst6,*B_ptrDst++[1]

          add    .l1    A_src4,A_src5,A_dst4               ;6

       ||  add    .l2    B_src8,B_src9,B_dst8

       ||  shru   .s1    A_dst3,1,A_dst3

       ||  shru   .s2    B_dst7,1,B_dst7

          add    .s1    A_dst4,A_round,A_dst4             ;7

       ||  add    .s2    B_dst8,B_round,B_dst8

       ||  ldbu   .d1t1  *+A_ptrSrc[2],A_src3

       ||  ldbu   .d2t2  *+B_ptrSrc[2],B_src7

       shru   .s1    A_dst4,1,A_dst4                    ;8

    ||  shru   .s2    B_dst8,1,B_dst8

    ||  stb    .d1t1  A_dst3,*A_ptrDst++[1]

||  stb    .d2t2  B_dst7,*B_ptrDst++[1]

          ldbu   .d1t1  *+A_ptrSrc[3],A_src4                ;9

       ||  ldbu   .d2t2  *+B_ptrSrc[3],B_src8

          stb   .d1t1   A_dst4,*A_ptrDst++[A_DstAdjust]     ;10

       ||  stb   .d2t2   B_dst8,*B_ptrDst++[B_DstAdjust]

这个优化程序是应用C6000公共指令集的最佳优化,它将C语言函数循环体的内循环进行了展开,采用分段读取源操作数的方法,即同时以一次内循环中8个数据的第一、第五个字节开始,使用ldbu指令读取数据,这样可以比全部从边侧读取字节数据的方法节省一个内核周期。数据处理完后使用stb指令存储数据。最终该程序的内核长度为10个周期,执行次数为七次,整个函数占用88个指令周期
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。