专家
公告
财富商城
电子网
旗下网站
首页
问题库
专栏
标签库
话题
专家
NEW
门户
发布
提问题
发文章
TI
c6000系列的C代码优化总结
2019-08-06 16:48
发布
×
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
站内问答
/
TI MCU
11245
15
1149
C6X_Optimizing_summarization.pdf
(331.96 KB, 下载次数: 182)
2012-12-11 14:40 上传 点击文件名下载附件
c6000系列的C代码优化总结
友情提示:
此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
15条回答
五谷道场
2019-08-07 05:22
十一、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 = hi1<<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);
// * --ound(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。
加载中...
查看其它15个回答
一周热门
更多
>
相关问题
CPLD的方波输出
4 个回答
11个版本Quartus II 软件下载,安装包网盘合集,附教程,47G!
20 个回答
请大家帮忙到21IC发展大家谈支持我申请新版面
20 个回答
【通知】21ic中国电子网服务条款 (所有人员必读)
1 个回答
满载而归乙亥年,大展鸿途庚子年---集签赢好礼
20 个回答
相关文章
×
关闭
采纳回答
向帮助了您的知道网友说句感谢的话吧!
非常感谢!
确 认
×
关闭
编辑标签
最多设置5个标签!
TI
保存
关闭
×
关闭
举报内容
检举类型
检举内容
检举用户
检举原因
广告推广
恶意灌水
回答内容与提问无关
抄袭答案
其他
检举说明(必填)
提交
关闭
×
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
×
付费偷看金额在0.1-10元之间
确定
×
关闭
您已邀请
0
人回答
查看邀请
擅长该话题的人
回答过该话题的人
我关注的人
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 = hi1<<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);
// * --ound(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。
一周热门 更多>