关于优化的一点小总结

    技术2022-05-20  60

     memory aliasing(存储器别名使用)

    编译器必须假设不同的指针可能会指向存储器中的同一个位置。这造成了一个主要的妨碍优化的因素。

    例如:

    void twiddle1(int *xp, int *yp)

    {

    *xp += *yp;

    *xp += *yp;

    }

    void twiddle2(int *xp, int *yp)

    {

    *xp = 2 * (*yp);

    }

    看起来twiddle1与twiddle2实现的是同样的功能,并且twiddle2应该是twiddle1的优化版,因为twiddle2只需要访问一次xp和一次yp,而twiddle1却用了两次。

    但是,考虑下面一种情况:

    int t;

    twiddle1(&t, &t);

    twiddle2(&t, &t);

    它们得到的结果是一样的吗,不!这也就是编译器在做优化的时候必须要考虑到的问题,编译器并不能完全了解编程者的意图,所以只能对当前的代码保守的优化。

    同样的例子还有:

    int counter = 0;

    int f(int x)

    {

    return (counter+x);

    }

    int f1(int x)

    {

    return f(x)+f(x);

    }

    int f2(int x)

    {

    return 2*f(x);

    }

    如何表示程序的性能—CPE/

    CPE: cycles per element, 每元素的同期数。

    如何理解,比如一个数组int array[50],它在函数f()中被用于计算,最后f()所用去的cpu clock为100, 那么函数f()的CPE为100/50=2.0;

    为什么不采用每次循环的周期数而是采用每元素的同期数呢,因为可能会出现循环展开的情况。

    如何消除循环的低效率

    Ø 减少不必要的函数调用:

    如for (i = 0; i < vec_length(v) i++)中的vec_length完全可以放在循环体的外面。

    Ø 消除不必要的存储器引用

    for (i = 0; i < length; i++)

    {

    *dest = *dest + data[i];

    }

    可以先用局部变量进行运算,最后才赋值到dest中去。

    Ø 作循环展开,让软件流水

    注意,让软件能流水的前提条件是在循环体中不能有判断语句,一个if也不行。所以要想让代码执行得更快,就要尽量想办法把判断语句搬移到循环体外面去。

    减少浮点运算所带来的开销

    一般来说,处理器作定点运算的速度会比作浮点运算的速度快(专用的浮点处理器例外)。如在DM642上,作定点运算的速度是作浮点运算速度的10倍。所以在处理浮点数之间,先将其转化为定点后运算再赋值将会获得很大的性能提升。

    是否应该将数组转化为指针代码

    根据经验,数组代码将会比指针代码更可取,我们已经看到过的编译器,它们对数组代码应用非常高级的优化,而对指针代码只应用最小限度的优化。并且数组代码具有更好的可读性。

    合理利用CACHE

    对于DM642而言,L2 Cache的大小是可以设定的,但如果Cache全开,将会用尽所有的L2 SRAM。这样就不能使用如DMA two buffer的技术了。但如果一点都不开,那对程序的运行速度是有非常大的影响的。


    最新回复(0)