打印

DSP 优化心得 (二)

[复制链接]
405|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
FCCdsp|  楼主 | 2017-10-11 17:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
DSP 优化心得


九、
1、源程序
  for (i = 0; i < n; i++)
  {
   max = -32767;
   for (j = 0; j < n; j++)
   {
    if (sub (tmp2[j], max) >= 0)
    {
      max = tmp2[j];
      ix = j;
    }
   }
   tmp2[ix] = -32768;
   tmp
= ix;
  }
2、优化后的程序
if (n0>n1) {temp=n0;n0=n1;n1=temp;}
if (n1>n2) {temp=n1;n1=n2;n2=temp;}
   if (n2>n3) {temp=n2;n2=n3;n3=temp;}
   if (n3>n4) {temp=n3;n3=n4;n4=temp;}
   if (n0>n1) {temp=n0;n0=n1;n1=temp;}
   if (n1>n2) {temp=n1;n1=n2;n2=temp;}
   if (n2>n3) {temp=n2;n2=n3;n3=temp;}
   if (n0>n1) {temp=n0;n0=n1;n1=temp;}
   if (n1>n2) {return n1;}
3、优化说明
  源程序也为一个求中值的问题,由于已知循环次数固定为5,因此将循环展开使用if语句直接求取中值。
十、
1、源程序
static Word16 Bin2int (Word16 no_of_bits, Word16 *bitstream)
{
Word16 value, i, bit;

value = 0;
for (i = 0; i < no_of_bits; i++)
{
value = shl (value, 1);
bit = *bitstream++;
if (sub (bit, BIT_1) == 0)
value = add (value, 1);
}
return (value);
}

for (i = 0; i < prmno[mode]; i++)
{
prm
= Bin2int (bitno[mode], bits);
bits += bitno[mode]
;
}
2、优化后的程序
value = 0;
  bitsp = bits;
  bitnop= &bitno[mode][0];
j = *bitnop++;
j1 = *bitnop++;
j2 = *bitnop++;
j3 = *bitnop++;
j4 = *bitnop++;
_nassert(loop[mode]>=35);
for (i = 0; i < loop[mode]; i++)
{
value = value*2 + *bitsp++;
j--;
if (j == 0)
{
*prm++ = value;
value = 0;
j = j1;
j1 = j2;
j2 = j3;
j3 = j4;
j4 = *bitnop++;
}
}
3、优化说明
  源程序按照数据位流定义取出参数,为双重循环结构,优化中采用重新根据位流的bit长度定义循环次数,
  化简为单重循环,然后优化循环,去除boundary,使pipeline的数目最小。

十一、copy程序的优化
  1、源代码:
Word16 i;
for (i = 0; i < L; i++)
{
y
= x;
}
  2、改编代码:
(1)要求数组长度能被2整除
Word32  i;
Word32   temp;
int *p1 = (int *)&x[0];
int *q1 = (int *)&y[0];
for (i = 0; i < L/2; i++)
{
temp = *p1++;
*q1++ = temp;
}
(2)要求数组长度能被4整除
Word32  i;
Word32   temp1, temp2;
Word32   *pin1, *pin2, *pout1, *pout2;
pin1 = (Word32 *)&x[0];
pin2 = (Word32 *)&x[2];
pout1= (Word32 *)&y[0];
pout2= (Word32 *)&y[2];
for (i = 0; i < L/4; i++)
{
temp1 = *pin1;
temp2 = *pin2;
pin1+=2;
pin2+=2;
*pout1= temp1;
*pout2= temp2;
pout1+=2;
pout2+=2;
}
3、优化方法说明:
把一次循环拷贝一个word16的数改为一次循环拷贝2个word16或4个word16的数。
4、技巧:
充分利用c6xx一次读取32位数的特性,并利用一个指令周期能读取两个数据的特点。
十二、set_zero程序的优化
  1、源代码:
Word16 i;
for (i = 0; i < L; i++)
{
x
= 0;
}
  2、改编代码:
(1)数组长度能被2整除
Word32 i;
int *x1 = (int *)&x[0];
for (i = 0; i < L/2; i++)
{
*x1++ = 0;
}
(2)数组长度能被4整除
Word32 i;
int *x1 = (int *)&x[0];
int *x2 = (int *)&x[2];
for (i = 0; i < L/4; i++)
{
*x1 = 0;
*x2 = 0;
x1++;
x2++;
x1++;
x2++;
}
3、优化方法说明:
把一次循环为一个word16的数赋值改为一次为2个或4个word16的数赋值。
4、技巧:
充分利用C6XX一次读取32位数的特点,并利用一个指令周期能读取两个数据的特点。
十三、32bit数与16bit数相乘
1、源代码:
L_tmp0 = Mac_32_16(L_32, hi1, lo1, lo2);
2、改编代码:
L_tmp0=_sadd(_sadd(_smpyhl(hl32, lo2),
  (_mpyus(hl32, lo2)>>16)<<1), L_32);
3、优化方法说明:
  hl32是32bit的数,hi1和lo1是16bit的数,且 hl32 = hi 1<<16 + lo1 << 1 ,即hi1和lo1分别是hl32的高16位数和低16位数。
  函数Mac_32_16(L_32, hi1, lo1, lo2)实现
    L_32 = L_32 + (hi1*lo2)<<1 + ((lo1*lo2)>>15)<<1
  源代码是把一个32位的数拆成两个16位的数与一个16位的数相乘,优化后的代码不拆开32位的数,
  直接用32位的数与16位的数相乘。运用这种方法必须保证hl32的最低一位数必须为0,否则应用指令_clr(hl32, 0, 0)把
  最低位清零。
4、技巧:
  源代码中的低16位数lo1是hl32的低16位右移一位得到的(留出一位符号位)。在与lo2相乘时又右移了15位,
  所以在改编代码中右移16位,并且是以无符号数与lo2相乘。
十四、32bit数与32bit数相乘
1、源代码:
L_tmp = Mac_32 (L_32, hi1, lo1, hi2, lo2);
2、改编代码:
  L_tmp = _sadd(_sadd(_smpyh(hl1_32, hl2_32),
      ((_mpyhslu(hl1_32, hl2_32)>>16)<<1)+
      ((_mpyhslu(hl2_32, hl1_32)>>16)<<1)), L_32);
3、优化方法说明:
  两个32位的数相乘,不必分成四个16位的数相乘,直接用32位相乘。其中:
    hl1_32 = hi1<<16 + lo1<<1, hl2_32 = hi2 <<16 + lo2 <<1 。
源代码实现: L_32 = L_32 + (hi1*hi2)<<1 + ( (hi1*lo2)>>15 + (lo1*hi2)>>15 )<<1
4、技巧:
低16位与高16位相乘时,低16位使用的是无符号数。
十五、16位除法的优化
1、源代码:
Word16 div_s (Word16 var1, Word16 var2)  //实现 var1/var2
{
Word16 var_out = 0;
Word16 iteration;
Word32 L_num = (Word32)var1;
Word32 L_denom = (Word32)var2;
for (iteration = 0; iteration < 15; iteration++)
{
var_out <<= 1;
L_num <<= 1;
if (L_num >= L_denom)
{
L_num = L_sub (L_num, L_denom);
var_out = add (var_out, 1);
}
}
return (var_out);
}
2、改编代码:
Word16 div_s1 (Word16 var1, Word16 var2)
{
Word32 var1int;
Word32 var2int;
var1int = var1 << 16;
var2int = var2 << 15;
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
return (var1int & 0xffff);
}
3、优化方法说明:
实现16位的除法,要求被除数var1和除数var2都是整数,且var1<=var2。利用C6XX特有的指令subc,实现除法的循环移位相减操作。
4、技巧:
把被除数和除数都转换成32位数来操作,返回时取低16位数。
十六、C6X优化inline举例:

1、原程序:
  for (i = LO_CHAN; i <= HI_CHAN; i++)
  {

    norm_shift = norm_l(st->ch_noise
);
    Ltmp = L_shl(st->ch_noise
, norm_shift);

    norm_shift1 = norm_l(st->ch_enrg
);
    Ltmp3 = L_shl1(st->ch_enrg
, norm_shift1 - 1);

    Ltmp2 = L_divide(Ltmp3, Ltmp);
    Ltmp2 = L_shr(Ltmp2, 27 - 1 + norm_shift1 - norm_shift);  // * scaled as 27,4 *

    if (Ltmp2 == 0)
      Ltmp2 = 1;

    Ltmp1 = fnLog10(Ltmp2);
    Ltmp3 = L_add(Ltmp1, LOG_OFFSET - 80807124);  // * -round(log10(2^4)*2^26 *
    Ltmp2 = L_mult(TEN_S5_10, extract_h(Ltmp3));
    if (Ltmp2 < 0)
      Ltmp2 = 0;
    // * 0.1875 scaled as 10,21 *
    Ltmp1 = L_add(Ltmp2, CONST_0_1875_S10_21);
    // * tmp / 0.375 2.667 scaled as 5,10, Ltmp is scaled 15,16 *
    Ltmp = L_mult(extract_h(Ltmp1), CONST_2_667_S5_10);
    ch_snr
= extract_h(Ltmp);
  }
  */
  
  
  
2、优化后程序:
  //因循环体太大,拆成两个循环并把相应的函数内嵌以使程序能pipeline,
  //用L_div_tmp[]保存因拆分而产生的中间变量。
  for (i = LO_CHAN; i <= HI_CHAN; i++)
  {
    //norm_shift = norm_l(st->ch_noise
);
    norm_shift = _norm(st->ch_noise
);
    Ltmp = _sshl(st->ch_noise
, norm_shift);

    //norm_shift1 = norm_l(st->ch_enrg
);  
    norm_shift1 = _norm(st->ch_enrg
);   
    //Ltmp3 = L_shl1(st->ch_enrg
, norm_shift1 - 1);
    LLtmp1 = st->ch_enrg
;   
    LLtmp1 = LLtmp1 << (norm_shift1 + 7);
    Ltmp3 = (Word32)(LLtmp1 >> 8);

    Ltmp2 = IL_divide(Ltmp3, Ltmp);
    //Ltmp2 = L_shr(Ltmp2, 27 - 1 + norm_shift1 - norm_shift);   
    Ltmp2 = (Ltmp2 >> (27 - 1 + norm_shift1 - norm_shift));

    if (Ltmp2 == 0)
      Ltmp2 = 1;
    L_div_tmp
= Ltmp2;
  }
  for (i = LO_CHAN; i <= HI_CHAN; i++)
  {
    Ltmp2 = L_div_tmp
;
    Ltmp1 = IfnLog10(Ltmp2);
    //Ltmp3 = L_add(Ltmp1, LOG_OFFSET - 80807124);  
    Ltmp3 = _sadd(Ltmp1, LOG_OFFSET - 80807124);
    //Ltmp2 = L_mult(TEN_S5_10, extract_h(Ltmp3));
    Ltmp2 = _smpy(TEN_S5_10, (Ltmp3 >> 16));
    if (Ltmp2 < 0)
      Ltmp2 = 0;
   
    Ltmp1 = _sadd(Ltmp2, CONST_0_1875_S10_21);
   
    //Ltmp = L_mult(extract_h(Ltmp1), CONST_2_667_S5_10);
    Ltmp = _smpy((Ltmp1 >> 16), CONST_2_667_S5_10);   
    //ch_snr
= extract_h(Ltmp);
    ch_snr
= (Ltmp >> 16);
  }
  
3、优化说明
  观察上面这个循环,循环体本身比较大,且含有两个函数L_divide()和
  fnLog10(),而C62内部只有32个寄存器,且有些寄存器是系统用的,如B14、B15这样循环体太大将会导致寄存器不够分配,
  从而导致系统编译器无法实现循环的pipeline。
  
  为了实现循环的pipeline。我们需要把循环体进行拆分,拆分时要考虑以下几点:
  (1)、拆分成几个循环比较合适?在各个循环能pipeline的前提下,拆开的循环个数越少越好。这就要求尽可能让各个
  循环的运算量接近。
  (2)考虑在什么地方把程序拆开比较合适?循环体里的数据流往往并不是单一的,在拆开的断点处势必要用中间变量保
  存上次的循环运算结果,供以后的循环用。适当的拆开循环体,使所需的中间变量越少越好。
  (3)循环体中的函数调用必须定义成内嵌形式,含有函数调用的循环系统是无法使之pipeline的;各个循环体中的判断分支
  机构不可太多,否则系统也无法使之pipeline,为此应近可能把可以确定下来的分支确定下来,并尽可能用内嵌指令。  
  
  针对上面这个例子,考虑:
  (1)为让各个循环的运算量大致相当,应把L_divide()和fnLog10()分到两个循环中去,从循环体大小上考虑,
  估计拆成两个循环比较合适。
  (2)考虑在什么地方把程序拆开比较合适?在
    if (Ltmp2 == 0)
      Ltmp2 = 1;
后拆开,因为后面用到的数据只有Ltmp2,故只需用一个数组保存每次循环的Ltmp2值即可。
  (3)循环体中的两处函数调用L_divide()和fnLog10()都定义了其内嵌形式,IL_divide()和IfnLog10()。
  当把可以确定下来的分支作确定处理,并尽可能用内嵌指令后,该循环体中所剩的分支结构已很少,循环体可以pipeline。
  优化前程序用2676 cycle,优化后用400 cycle。优化后两个子循环的MII分别为14和6cycle。

内存地址形式: 奔腾,C6000都是32位计算机,字长32,但内存地址都是按字节组织的 一个字4字节(查看内存时候各个字
时候:例如两个连续字ox1000 ox1004) 写汇编程序时候,下一个字也需要+4,但写 C语言时候,int 型,+1就是加4

但是,在Tiger SHARC中,虽然也是32位机,但内存是地址是按字组织的,查看内存时,连续的字地址相差1
//////////////////////////////////////////////////////////////////////////////////自己写的一段性能很高的代码///////////////////////////
#include <stdio.h>
#define INTRINSIC
short add(short var1,short var2)
  {
   short var_out;
   int L_somme;
   L_somme = (int) var1 + var2;
   return(var_out);
  }
  
int main()
{
int i,result;
#ifdef INTRINSIC
for(i=0; i<1000;i++)
{
result=_sadd(100000,20);
result>0X00007fff?result=0x7fff:(result<0x8000?result=0x8000:0);
}
#else
for(i=0;i<1000;i++)
add(10,20);
#endif
return 0;
}



相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

967

主题

1447

帖子

9

粉丝