下面是合成滤波器(Synthesis Filter)的一个优化过程, 合成滤波器在语音编解码中有广泛应用, 运行时也占用了整个算法中较高比例的时间。
1
2
3
4
5
6
7
8
9
10
11
| for (i = 0; i < lg; i++)
{
s = L_mult(x, a[0]);/*L_mult是相乘后左移*/
for (j = 1; j <= M; j++){/*M这里固定为10*/
s = L_msu(s, a[j], yy[-j]);/*L_msu是乘减后左移操作*/
}
s = L_shl(s, 3); /*左移三位*/
*yy++ = g729round(s);
}
#endif
|
上面的代码,因为内存循环为10,可以考虑展开,并统一操作为乘加指令。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| /*为了使用乘加操作,需要调整10个系数的顺序*/
for(i = 0; i < M; i++)
ta = -a[M - i];
ta[11] = 0;
ta[10] = a[0];
for (i = 0; i < lg; i++){
*yy = x;
yy[1] = 0;
s = L_mac(s, ta[11], yy[1]);
s = L_mac(s, ta[10], yy[0]);
s = L_mac(s, ta[9], yy[-1]);
s = L_mac(s, ta[8], yy[-2]);
s = L_mac(s, ta[7], yy[-3]);
s = L_mac(s, ta[6], yy[-4]);
s = L_mac(s, ta[5], yy[-5]);
s = L_mac(s, ta[4], yy[-6]);
s = L_mac(s, ta[3], yy[-7]);
s = L_mac(s, ta[2], yy[-8]);
s = L_mac(s, ta[1], yy[-9]);
s = L_mac(s, ta[0], yy[-10]);
s = L_shl(s, 3);
*yy++ = g729round(s);
}
|
以上循环内核正好可以将MMX的8个寄存器全部利用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| /*为了使用乘加操作,需要调整10个系数的顺序*/
for(i = 0; i < M; i++)
ta = -a[M - i];
ta[11] = 0;
ta[10] = a[0];
/*11个系数分别放入3个MMX寄存器,0作填充*/
asm("movq %0,%%mm0;\
movq %1,%%mm1;\
movq %2,%%mm2"\
:\
: "m" (ta[0]), "m" (ta[4]), "m"(ta[8]));
/*利用MMX技术进行滤波器核心操作*/
for (i = 0; i < lg; i++){
*yy = x;
yy[1] = 0;
asm("pandn %%mm6,%%mm6;\
movq %1,%%mm3;\
movq %2,%%mm4;\
movq %3,%%mm5;\
pmaddwd %%mm0,%%mm3;\
pmaddwd %%mm1,%%mm4;\
pmaddwd %%mm2,%%mm5;\
paddd %%mm3, %%mm6;\
paddd %%mm4, %%mm6;\
paddd %%mm5, %%mm6;\
movq %%mm6, %%mm7;\
psrlq $32, %%mm6;\
paddd %%mm7, %%mm6;\
movd %%mm6,%0;\
emms"
:
:"r"(s), "m" (yy[-10]), "m" (yy[-6]), "m"(yy[-2]));
/*因为指令结果饱和属性的限制,s还没有左移,所以下面多做一位饱和左移*/
s = L_shl(s, 4);
*yy++ = g729round(s);
}
|
几点说明:
- 注意:以上嵌入的汇编代码输出结果s放在了输入处,属于实践中的个案;
- MMX没有乘左移之类的DSP指令,甚至还没有加饱和之类的操作,SSE中有一定增强;
- 以上操作,理论上存在溢出可能,所以最后使用原有的饱和左移操作,减少了一定风险;
- 上面的部分代码操作显然允许并行,这在VLIW系统中十分有用;
- 这已经形成了该滤波器全面优化的核心。
|