1、一般编写代码首先是用C,基于C层面优化的方法,我如下举例说几种: (1)优化循环 for(i = 0;i < max;i ++) { for(j = 0; j < max; j ++) { float sum = 0.0; for(k = 0; k < max; k ++) { sum += input[i * max + k] * input[j * max + k]; } cover[i * max + j] = sum / max; } } 实例,如input[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16},得出的cover如下: 7 17 27 37
17 43 69 95
27 69 111 153
37 95 153 211 图示:
原理:只需要得到左上角或右上角即可,然后半个矩阵赋值给另半个矩阵即可得到整个矩阵。 算法优化后: for(i = 0; i < max; i ++) { for(j = i; j < max; j ++) // 减少一半循环 { float sum = 0.0; for(k = 0; k < max; k ++) { sum += input[i * max + k] * input[j * max + k]; } cover[i * max + j] = sum / max; if(i != j) // 可加可不加,消除中线上的重复赋值 { cover[j *max + i] = sum / max; // 赋值给另半个矩阵 } } } (2)条件跳转(使用条件跳转会在流水线中浪费更多的周期) k = k -1; if(k < -1) { k = -1; } 原理:C语言中的max函数在编译的过程中实际上实现的是DSP中的MAX指令。 优化后: k = max(k-1, -1); 转换成汇编后: R0 += -1; // R0 == k; R1 = -1; R0 = MAX(R0, R1); (3)for循环中的条件跳转 for { if{...}else{...} } 原理:减少频繁的条件跳转,当然并不是所有的情况都可以这样做。 优化后: if { for{...} } else { for{...} } (4)使用断言指令来避免条件跳转 if(A) { X = exp1; } else { X = exp2; } 原理:使用断言指令IF(CC) REG = REG,只会消耗一个周期。 优化后: X = exp1; if(!A) { X = exp2; } (5)除法(取模)操作 一般DSP中不支持除法,除法操作是通过仿真的方式来进行实现,有两种,分为低精度和高精度,但都需要相大多的周期。 除数为2的N次方时可采用右移法。 如为提高性能,可采取查表的方式,这样会损失精度。 隐藏的除法: for(i = start; i < finish; i += step) 此时编译器会looper = (finish - start) / step 得到次数。 巧妙利用不等式法测:(不过可能会产生溢出,要小心使用) if(x / y > a / b) 可转换为: if(x * b > a * y) (6)数据类型 对于定点DSP而言,对于浮点的操作是用仿真的方式实现的,会消耗很多周期,所以在定点DSP上对于浮点数一般是做定点化的处理,常用的方法我举一个例子:2.5 * a,其实可以转换成80 * a >> 5,不过在定点化时需要注意防溢出。 对于64位数据,也是用仿真的方式实现的,会消耗很多周期。(一般最大仅支持32位数据) 做数字信号处理操作时,如FFT,16bit的操作是比较合适的。 要做控制类,条件跳转时,32bit的操作是比较合适的。 如你的DSP的MAC是16位的,做乘法时,尽量定义成16bit数据。 (7)Memory分配 将运算比较频繁的数据和程序段放入片内Memory,开启cache。 如DSP能对SDRAM的不同4个bank可以同时访问,此时你可以将需要同时运算的数据放入不同的bank (8)开启仿真软件的编译优化选项 在菜单相应的地方勾上即可,但值得注意时,开启自动编译优化选项后,可能会使执行的结果发生变化,所以需要测试对比一下未开编译优化选项之前的执行结果,一般来说,这个很方便,比较常用。 以上8种是我常用到的优化方法,当然基于C层面算法类的优化还有很多种,这个需要慢慢积累,总结一下,一般来说先对C层面进行结构上的优化(上面的1-6均属于),然后进行Memory分配,开启仿真软件的编译优化选项,将运算频繁的程序段用汇编实现,当然如果性能满足要求,就没必要利用汇编了 |