Blackfin提供了一组数据访问寄存器(DAG)用于提高对数据的访问效率,其中Circular Buffer是其一大特色。在汇编中,我们可以对它轻易地控制,但是在C语言下,并无法显式地使用它们。尽管如此,还是可以通过一些内建函数与编译选项提高数据的访问效率。
Circular Buffer,即循环缓存,也就是指规定一段内存,假设起始地址为B,终止地址为B + L,当指针I大于B+L时,将自动将I调整为B + I % L。
先看以下这个例子:
2 {
3 b[i % 80 ] = i;
4 }
5
一般的CPU都没有硬件支持%操作,它实际上是用子程序完成的,效率上非常低。而Blackfin DSP提供的DAG通过合理设计,可以实现%操作。
为了加深一下理解,这里贴出一个手写的汇编例子及其对应的C代码:
.section L1_data ;
.byte str [] = ' the number:%d ' , 10 ;
.section program ;
.align 8 ;
.global _main ;
_main:
link 12 ;
p0 = 0 ;
b0 = p0 ;
p1 = 9 ;
l1 = 4 ;
i1 = 0 ;
m1 = 1 ;
lsetup(_main.l1, _main.l1.end) lc0 = p1 ;
_main. l1:
i1 += m1 ;
_main.l1. end:
r1 = i1 ;
r0.l = lo( str ) ;
r0.h = hi( str ) ;
call _printf ;
unlink ;
RTS ;
_main. end:
对应的C代码:
int main( void )
{
int x = 0 ;
int i;
for (i = 0 ; i < 9 ;i ++ )
x = (x + 1 ) % 4 ;
printf( " the number:%d\n " , x);
return 0 ;
}
输出结果:
好的,接下来我们谈谈如何在C语言中优化。
一种方法在编译时指定-force-circbuf选项,强制将所有循环索引和指针都用DAG实现,使用这种方法,程序员应该自己保证索引和指针总是在循环缓存范围内(即I应该总能在B和L之间)。
Visual DSP++的该选项位置:
另一种方法,也是推荐的方法,是使用内建函数指导编译器优化。Blackfin提供了两个函数,它们的头文件是ccblkfn.h。
循环索引:
循环指针:
首先看循环索引的例子,及其反汇编代码来说明如何优化:
#include < stdio.h >
int b[ 1000 ];
int sum;
int main( void )
{
int i, j, n = 20 ;
for (i = 0 ;i < 1000 ; i += n)
{
sum += (i % 80 );
}
for (i = 0 , j = 0 ; i < 1000 ; i += 20 )
{
sum += j;
j = __builtin_circindex(j, 20 , 80 );
}
return 0 ;
}
图中两个标注的地方分别是两个for语句内代码的反汇编,可以看出,第一个编译器并无优化,里面还用CALL调用了子函数。相比之下,第二个for每次循环只有2周期,效率相差至少5倍。
再看另一个有关循环指针的例子:
#include < stdio.h >
int b[ 1000 ];
int sum;
int main( void )
{
int i, j, n = 20 ;
int * p;
for (i = 0 ; i < 1000 ; i += n)
{
b[i % 80 ] = i;
}
p = b;
for (i = 0 ; i < 1000 ; i += n)
{
* p = i;
p = __builtin_circptr(p, n ,b, 80 );
}
return 0 ;
}
从截图看到,两个for反汇编后的代码是一样的,说明编译器认为第一个for是可以安全优化的。
结语:尽管编译器可以自动对代码优化,但是并不是所有可以优化的地方都能检测出来,因此,有觉得有必要优化的地方,使用内建函数对编译器指导指导还是必要的。建议不使用-force-circbuf,在较大项目里,它使调试变得更困难。