单片机|既要代码小,又想速度快!单片机程序该如何优化?( 二 )


1.8 尽量少用全局变量 , 多用局部变量
因为全局变量是放在数据存储器中 , 定义一个全局变量 , MCU 就少一个可以利用的数据存储器空间 , 如果定义了太多的全局变量 , 会导致编译器无足够的内存可以分配;而局部变量大多定位于MCU 内部的寄存器中 , 在绝大多数MCU 中 , 使用寄存器操作速度比数据存储器快 , 指令也更多更灵活 , 有利于生成质量更高的代码 , 而且局部变量所的占用的寄存器和数据存储器在不同的模块中可以重复利用 。
1.9 设定合适的编译程序选项
许多编译程序有几种不同的优化选项 , 在使用前应理解各优化选项的含义 , 然后选用最合适的一种优化方式 。 通常情况下一旦选用最高级优化 , 编译程序会近乎病态地追求代码优化 , 可能会影响程序的正确性 , 导致程序运行出错 。 因此应熟悉所使用的编译器 , 应知道哪些参数在优化时会受到影响 , 哪些参数不会受到影响 。
二、代码的优化
2.1 选择合适的算法和数据结构
应熟悉算法语言 。 将比较慢的顺序查找法用较快的二分查找法或乱序查找法代替 , 插入排序或冒泡排序法用快速排序、合并排序或根排序代替 , 这样可以大大提高程序执行的效率 。
选择一种合适的数据结构也很重要 , 比如在一堆随机存放的数据中使用了大量的插入和删除指令 , 比使用链表要快得多 。 数组与指针具有十分密切的关系 , 一般来说指针比较灵活简洁 , 而数组则比较直观 , 容易理解 。 对于大部分分的编译器 , 使用指针比使用数组生成的代码更短 , 执行效率更高 。
但是在Keil 中则相反 , 使用数组比使用的指针生成的代码更短 。
2.2 使用尽量小的数据类型
能够使用字符型(char)定义的变量 , 就不要使用整型(int)变量来定义;能够使用整型变量定义的变量就不要用长整型(long int) , 能不使用浮点型(float)变量就不要使用浮点型变量 。 当然 , 在定义变量后不要超过变量的作用范围 , 如果超过变量的范围赋值 , C 编译器并不报错 , 但程序运行结果却错了 , 而且这样的错误很难发现 。
2.3 使用自加、自减指令
通常使用自加、自减指令和复合赋值表达式(如a-=1 及a+=1 等)都能够生成高质量的程序代码 , 编译器通常都能够生成inc 和dec 之类的指令 , 而使用a=a+1 或a=a-1之类的指令 , 有很多C 编译器都会生成2~3个字节的指令 。
2.4 减少运算的强度
可以使用运算量小但功能相同的表达式替换原来复杂的的表达式 。 如下:
(1)求余运算
a=a%8;
可以改为:
a=a&7;
说明:位操作只需一个指令周期即可完成 , 而大部分的C 编译器的“%”运算均是调用子程序来完成 , 代码长、执行速度慢 。 通常 , 只要求是求2n 方的余数 , 均可使用位操作的方法来代替 。
(2)平方运算
a=pow(a2.0);
可以改为:
a=a*a;
说明:在有内置硬件乘法器的单片机中(如51 系列) , 乘法运算比求平方运算快得多 , 因为浮点数的求平方是通过调用子程序来实现的 , 在自带硬件乘法器的AVR 单片机中 , 如ATMega163 中 , 乘法运算只需2 个时钟周期就可以完成 。 既使是在没有内置硬件乘法器的AVR单片机中 , 乘法运算的子程序比平方运算的子程序代码短 , 执行速度快 。 如果是求3 次方 , 如:
a=pow(a3.0);
更改为:
a=a*a*a;
则效率的改善更明显 。
(3)用移位实现乘除法运算
a=a*4;
b=b/4;
可以改为:
a=a<<2;
b=b>>2;
说明:通常如果需要乘以或除以2n , 都可以用移位的方法代替 。 在ICCAVR 中 , 如果乘以2n , 都可以生成左移的代码 , 而乘以其它的整数或除以任何数 , 均调用乘除法子程序 。 用移位的方法得到代码比调用乘除法子程序生成的代码效率高 。 实际上 , 只要是乘以或除以一个整数 , 均可以用移位的方法得到结果 , 如: