代码开销优化方法

最近作了一个代码优化相关的总结,这里记录一下,这里一般指的是DSP,ARM这类芯片,一般不涉及多核。


1. 三个不同的优化:

(1) 等价优化:如多余的计算,内存的访问次序等 ,不必要的计算,内存对齐等等,这些基本上对结果不会有改变,但是对开销影响可能会比较大;

(2) 非等价优化:如数学函数的等价优化、定点化等; 

(3) 利用器件特性的优化:如SIMD的优化,嵌入式汇编的使用。 


2. 优化的方法:

优化的最终目标是减少程序执行开销,那么任何能达到这个目的都是可取的。这里分别从开销涉及到的维度进行介绍:

(1) 内存访问:

      对于较大规模数据的运算,涉及到的内存访问会占用较大比重,尤其是数据在外存的情况下。

     1)减少内存访问;

     2)充分利用cache,减少内存访问miss;

     3)减少非连续内存访问;

     4) 变量内存对齐;

(2) 单运算单元:

    一般运算单元都可以除理+、-、x、位运算、逻辑运算、调转等功能,一般的DSP和ARM是不支持直接的/运算的,一般对于/、mod都是调用算学库来算的,开销较大。

    1)尽量少使用/、%这类指令,可以使用乘法、自定义除法、==、&=替代;

   2) 使用近似替代运算: 如sqrt(a^2+b^2) 用 max(abs(a),abs(b))或用其他平滑方法来作;

   3)使用开销小的等价运算替代:如log(sqrt(a^2+b^2)) = log(a^2+b^2) /2 = log(a^2+b^2)*0.5;

   4)  减少不必要的计算;

(3) SIMD类:这个就没啥 说的了,充分利用总线带宽,单指令实现多个数据的相同的操作,减少代码执行时间,如arm的NEON指令优化,TI DSP的SIMD指令,X86的SSE等。

(4) 汇编:

从代码维护角度而言,不建议编写纯汇编的函数或文件 ,一般只会在basic_op上使用部分嵌入式汇编。


3. 一些例子: 

(1) 能在循环外做的就在外面做:


code2与code1完全等价,但是把每次的乘累加变成了累加再乘,计算量更少。

能在循环外做的就在外面做,毕竟外面做的代价最小。

(2) 尽可能顺序访问:


以上两个代码的功能是相同的,都是作w * input的矩阵计算,但是code1中用的是非连续访问,cache miss很大, 而code2中对w_T是连续访问,开销会小很多。

(3) 减少内存访问:

一个比较常见的例子就是语音buffer,可能某些场景下,需要一个很大的buffer,那么就涉及到这个buffer的更新问题。

最简单的方法是:每次更新时把最新的压到最后,必然会涉及到历史数据的推移,当buffer较大时,就涉及到一个较大内存块的读取和写入;

比较经济的作法是循环buffer方式,需要有一个指针指向最新写入点的位置,读取或写入时都要根据这个位置来进行,而且会涉及到从buffer结尾切到buffer起始判断,但不可否认的是,这种方法将对内存声的考贝减到最少了。




猜你喜欢

转载自blog.csdn.net/u013412738/article/details/80639558
今日推荐