C语言代码优化编码
嵌入式设备对代码运行性能要求很高,因此在编写代码的时候应尽量人为的让代码的运行效率达到最优值,实际编码过程中是存在一定的编程经验的。同样的代码使用不同的编码方式实际的运行性能是由差别的。
- 表达式优化-替换乘法除法
实际代码运行过程中,CPU最喜欢的操作加减法操作,其执行时间只占用1个钟周期,是最快的操作。移位运算和位运算操作的速度和加减法接近。乘法执行的时间大概需要5-6个时钟周期,而除法的执行时间最慢,大约需要50个时钟周期。因此在执行代码编程的时候尽量减少乘法和除法的运算。
如要执行a = b*16的操作,使用a = b << 4;
如要执行 a= b/16的操作,使用a = b >> 4;
- 表达式优化-常量折叠
执行代码尽量不要写一个变量等于多个常量执行多种运算的方式。如:
A = 3+4*5;可以直接写成a = 23。但是这样的编程方式可读性比较差,不知道其具体含义,可以使用添加注释的方式表明其含义。这样的编码方式也许编译器已经给优化了,实际编程人员结合实际情况进行操作。个人推荐还是写为常量添加注释的方式比较好。
- 表达式优化-使用数学公式
这里说的数学公式是说一些表达式的计算符合一些定理的表达式,可以减少CPU运算次数。
如求一个100次循环0到100的求和,这时候编程如果使用循环的方式就是for循环100次的操作,这样CPU就要执行至少300次计算。针对这样的计算可以使用数学公式:高斯公式计算1到n之间的所有整数的和。
Sum = ((1 +n)*n)/ 2;这样就简化了计算流程。这样的优化方式需要结合实际的代码情况考虑是否可以使用数学公式去完成。
3.表达式优化-存储问题
C语言中的变量是存储在内存中的,实际运算过程中需要将变量的值从内存中读取出来存放到寄存器中然后再运算,但是访问内存就比较耗时,因此实际编程过程中尽量减少访问内存的操作。
实际编程过程中 a = a+1 是访问两次内存操作,=操作符CPU会认为两侧是两个操作数,因此实际计算操作是:
- 定位右侧变量a的地址,并将地址读取到寄存器中;
- 从地址中读取变量a的值;
- CPU计算a+1操作后的值并存在寄存器中;
- 确定左侧变量的地址并读取到寄存器中;
- 将计算结果存储到4中获取的地址中。
以上共5步完成运算。
而实际过程中使用 a+=1的方式会减少一次访问内存的操作,这样CPU运算只访问一次a的内存地址:
- 定位a的地址并将地址存放到寄存器中;
- 读取a的值到寄存器中;
- 计算+1操作;
- 将3的结果存放到a的地址中。
4步就完成了同样的操作。
- 分支优化-改变判断顺序
实际代码编程过程中if elseif else的条件编程肯定少不了,但是条件的判断顺序有时候也会影响代码运行的性能。If else if的语句执行逻辑是先判断第一个if如果第一个if判断条件不满足则判断下面的else if 如果还不满足则继续向下比较直到最后一个比较完成。这样就是越向下判断的次数就越多,因此实际编程过程中要依赖实际的场景将判断条件概率的分支放在最前面,这样的方式依赖于实际的场景了。
- 分支优化-使用switch语句
Switch的语句执行逻辑是代码分支比较只执行一次,不论有多少case 代码只比较一次。但是case的比较只能常量不能是范围内的判断。Switch的代码实现逻辑是编译器会生成一个跳转表,其实就是一个数组,根据case的索引值执行跳转到对应的地址执行对应的操作。实际的代码罗会相对复杂一些。如果if能用switch替换则使用switch语句可以有概率的提升代码执行效率。
7.循环优化-一次性计算
如果代码实现了循环操作,则这个循环体内的操作尽量都是需要修改的变量赋值操作,如果变量一直不变的就没有必要放在循环体内。
如while(I < 3)
{
A = 1;
C += 1;
}
这时的A = 1就没有必要放在循环体内,放在循环体外执行一次。
还有一个例子:
For (i = 0; i < a+3;i++)
{
……
}
这样的代码每一次循环操作都要执行一次a+3操作,可以将a+3操作放在循环体外执行一次就可以。
总结
优化道路无止境。上面介绍的执行一些常规的经验之谈,实际编程过程中遇到场景会很复杂。但有一个中心思想就对高频率执行的代码一定要保证其简洁性,还有就尽量减少对FLASH的操作因为擦写FLASH是最耗时的操作。大家编写代码的时候一定要结合实际场景做到心里有数。