• Blackfin DSP的C语言优化之Circular Buffer


    Blackfin提供了一组数据访问寄存器(DAG)用于提高对数据的访问效率,其中Circular Buffer是其一大特色。在汇编中,我们可以对它轻易地控制,但是在C语言下,并无法显式地使用它们。尽管如此,还是可以通过一些内建函数与编译选项提高数据的访问效率。

           Circular Buffer,即循环缓存,也就是指规定一段内存,假设起始地址为B,终止地址为B + L,当指针I大于B+L时,将自动将I调整为B + I % L

           先看以下这个例子:


    1     for (i =0; i < 1000; i += n)
    2     {
    3         b[i % 80= i;
    4     }
    5 

    一般的CPU都没有硬件支持%操作,它实际上是用子程序完成的,效率上非常低。而Blackfin DSP提供的DAG通过合理设计,可以实现%操作。

    为了加深一下理解,这里贴出一个手写的汇编例子及其对应的C代码:

      
    .import "stdio.h";
    .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代码:

    #include <stdio.h>
    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;
    }

      输出结果:

    the number:1

    好的,接下来我们谈谈如何在C语言中优化。

    一种方法在编译时指定-force-circbuf选项,强制将所有循环索引和指针都用DAG实现,使用这种方法,程序员应该自己保证索引和指针总是在循环缓存范围内(即I应该总能在BL之间)。

    Visual DSP++的该选项位置:


    另一种方法,也是推荐的方法,是使用内建函数指导编译器优化。Blackfin提供了两个函数,它们的头文件是ccblkfn.h

    循环索引:

    long circindex(long index, long incr, unsigned long nitems);

      循环指针:

    void *circptr(void *ptr, long incr, void *base, unsigned long buflen);

    首先看循环索引的例子,及其反汇编代码来说明如何优化:


    #include <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, 2080);
        }
        
    return 0;
    }



     

    图中两个标注的地方分别是两个for语句内代码的反汇编,可以看出,第一个编译器并无优化,里面还用CALL调用了子函数。相比之下,第二个for每次循环只有2周期,效率相差至少5倍。

    再看另一个有关循环指针的例子:

    #include <ccblkfn.h>
    #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)
        {
            
    *= i;
            p 
    = __builtin_circptr(p, n ,b, 80);
        }
        
        
    return 0;
    }


       从截图看到,两个for反汇编后的代码是一样的,说明编译器认为第一个for是可以安全优化的。

    结语:尽管编译器可以自动对代码优化,但是并不是所有可以优化的地方都能检测出来,因此,有觉得有必要优化的地方,使用内建函数对编译器指导指导还是必要的。建议不使用-force-circbuf,在较大项目里,它使调试变得更困难。


  • 相关阅读:
    乘法九九表
    #include <time.h>
    【bzoj2060】[Usaco2010 Nov]Visiting Cows拜访奶牛 树形dp
    【codevs1380】没有上司的舞会 树形dp
    【bzoj1060】[ZJOI2007]时态同步 树形dp
    【bzoj2435】[NOI2011]道路修建 树形dp
    【bzoj3573】[HNOI2014]米特运输 树形dp
    【bzoj4027】[HEOI2015]兔子与樱花 树形dp+贪心
    【codevs1163】访问艺术馆 树形dp
    【bzoj1864】[ZJOI2006]三色二叉树 树形dp
  • 原文地址:https://www.cnblogs.com/pheye/p/1891498.html
Copyright © 2020-2023  润新知