LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1192|回复: 12

关于循环的优化

[复制链接]
发表于 2004-8-2 10:43:38 | 显示全部楼层 |阅读模式
最近在做一个很简单的STEREO->MONO的变换函数的时候遇到了循环效率的问题。想了一下怎么优化之后,觉得有点意思,就写下来纪录一下吧。
算法的公式非常简单,MONO = (Lch + Rch) / 2
STEREO数据流中的数据是以[Lch Rch Lch Rch Lch Rch.....]这种格式存放的,一般来说,首先想到的变换方式是这样的

  1. for (i = 0; i < MONO_BUFFSIZE; i++){
  2.         MonoBuffer[i] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  3. }
复制代码

可是,我的STEREO缓冲区有5000个bytes,当然MONO_BUFFSIZE的大小就是它的一般,2500个bytes。那么上面这段代码就需要循环运算2500次。而众所周知,循环是系统资源的最大杀手。我的程序也毫不客气的占用了将近20%的CPU资源。不得不进行优化。稍微想了一下,我把上面那段代码修改成下面的样子。

  1. i = 0;
  2. while (i < MONO_BUFFSIZE){
  3.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  4.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  5.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  6.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  7.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  8.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  9.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  10.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  11.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  12.         MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;
  13. }
复制代码

也就是说,一次循环我要做10次的运算,那么本来2500次的循环就被我缩减到250次了。看了一下CPU资源,占用率缩小到了16%,还不是非常满意。然后又看了一下代码,发现了另外一个问题。因为MonoBuffer这个缓冲区中的数据要作为输入参数参与滤波函数的运算,而那个滤波函数要求输入参数必须是float类型的。所以我在运算的过程中加入了进行强制类型转换的符号:(float)。可是这个符号加入的意义在于,对STEREO参数的除法结果要同时保存商和余数两个部分。而我又想了一下,似乎余数部分作为滤波器输入函数,对结果的影响不是很大,可以不要。所以,我就把(float)给去掉了。果然,最后运行的时候CPU占用率下降到了12%左右。运算结果也和修改前没有很大出入。

需要提醒注意的是,因为我的缓冲区大小分别是5000和2500,都是10的倍数,所以我每次循环运算10次,没有问题。如果缓冲区的大小和每次循环运算次数不成倍数关系,那么可能会造成程序调用了非法内存地址,导致内存区域被破坏。实际应用中,每次循环的运算次数也不一定是越多越好,这个可以根据具体情况自己反复试验,以求达到最大效率。
另外,在采用类似上述代码的时候,一定要先了解清楚,当时使用的C语言解释器对类似MonoBuffer[i++] = (float)(StereoBuf[i * 2] + StereoBuf[i * 2 + 1]) / 2;这种语句是不是只有在全部运算完毕之后再进行i++自增运算的,否则就需要把i++单独列出来,作为一句独立的指令来运行。


--------转贴请保留名字,谢谢配合。
发表于 2004-8-2 22:03:26 | 显示全部楼层
好!!!!!!

支持Sworder多套些好东西回来
 楼主| 发表于 2004-8-3 06:27:28 | 显示全部楼层
什么套啊?这是我自己的经验~~~
发表于 2004-8-3 11:55:25 | 显示全部楼层
感谢Sworder与大家分享自己的经验!
发表于 2004-8-3 17:37:29 | 显示全部楼层
打开loop unrolling试试?-O2的情况下,这个默认是关闭的。
 楼主| 发表于 2004-8-3 18:18:07 | 显示全部楼层
我指的是广泛意义,过多依靠编译器来优化代码,我觉得不太好。
发表于 2004-8-6 11:33:08 | 显示全部楼层
自己对程序做优化,主要开始考虑算法方面,这种机械的,还是教给编译器吧。打开了loop unrolling,编译器和你做的活其实一样,呵呵,在汇编的层面上甚至比你更有效率。
当然优化不要太狠了,gcc自己都说3.4以前的会因为优化引入问题。
发表于 2004-8-6 13:08:08 | 显示全部楼层
不是所有编译器都能做到这个的
有些时候只靠编译器是不行的,
将军之所以成为将军,不但要有智谋,也需要他们自己也得相当能打
都承认Windows不稳定,但比尔盖茨在Basic语言的结构方面是个绝对的权威,别看人家是自学的

所以为了当将军自己多做一点没坏处
 楼主| 发表于 2004-8-6 13:39:21 | 显示全部楼层
我没有用gcc,嵌入式开发很多时候不是靠它的。
在我的编译器上面,我写的两种做法确实是产生了优化效果。所以,知道一下还是没有坏处滴,嘿嘿。。
发表于 2004-8-6 15:35:27 | 显示全部楼层
如果编译器没有loop unrolling的优化,那当然只有自己来了...
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表