对程序进行优化,一般是指优化程序代码或程序履行速度。优化代码和优化速度实践上是一个予盾的共同,一般是优化了代码的尺度,就会带来履行时刻的添加,假如优化了程序的履行速度,一般会带来代码添加的副作用,很难鱼与熊掌兼得,只能在规划时把握一个平衡点。
一、程序结构的优化
1、程序的书写结构
尽管书写格局并不会影响生成的代码质量,但是在实践编写程序时仍是应该尊循必定的书写规矩,一个书写明晰、明晰的程序,有利于今后的保护。在书写程序时,特别是关于While、for、do…while、if…elst、switch…case 等句子或这些句子嵌套组合时,应选用“缩格”的书写办法,
2、标识符
程序中运用的用户标识符除要遵从标识符的命名规矩以外,一般不要用代数符号(如a、b、x1、y1)作为变量名,应选取具有相关意义的英文单词(或缩写)或汉语拼音作为标识符,以添加程序的可读性,如:
count、number1、red、work 等。
3、程序结构
C 言语是一种高档程序规划言语,供给了非常齐备的规范化流程操控结构。因而在选用C 言语规划单片机运用体系程序时,首要要留意尽可能选用结构化的程序规划办法,这样可使整个运用体系程序结构明晰,便于调试和保护。于一个较大的运用程序,一般将整个程序按功用分红若干个模块,不同模块完结不同的功用。各个模块可以别离编写,乃至还可以由不同的程序员编写,一般单个模块完结的功用较为简略,规划和调试也相对简单一些。在C 言语中,一个函数就可以认为是一个模块。所谓程序模块化,不仅是要将整个程序划分红若干个功用模块,更重要的是,还应该留意坚持各个模块之间变量的相对独立性,即坚持模块的独立性,尽量少运用全局变量等。关于一些常用的功用模块,还可以封装为一个运用程序库,以便需求时可以直接调用。但是在运用模块化时,假如将模块分红太细太小,又会导致程序的履行功率变低(进入和退出一个函数时保护和康复寄存器占用了一些时刻)。
4、界说常数
在程序化规划过程中,关于常常运用的一些常数,假如将它直接写到程序中去,一旦常数的数值发生变化,就有必要逐个找出程序中一切的常数,并逐个进行修正,这样必然会下降程序的可保护性。因而,应尽量当选用预处理指令办法来界说常数,而且还可以防止输入过错。
5、削减判别句子
可以运用条件编译(ifdef)的当地就运用条件编译而不运用if 句子,有利于削减编译生成的代码的长度。
6、表达式
关于一个表达式中各种运算履行的优先次序不太清晰或简单混杂的当地,应当选用圆括号清晰指定它们的优先次序。一个表达式一般不能写得太杂乱,假如表达式太杂乱,时刻久了今后,自己也不简单看得懂,不利于今后的保护。
7、函数
关于程序中的函数,在运用之前,应对函数的类型进行阐明,对函数类型的阐明有必要确保它与本来界说的函数类型共同,关于没有参数和没有返回值类型的函数应加上“void”阐明。假假如需求缩短代码的长度,可以将程序中一些公共的程序段界说为函数,在Keil 中的高档别优化便是这样的。假如需求缩短程序的履行时刻,在程序调试完毕后,将部分函数用宏界说来替代。留意,应该在程序调试完毕后再界说宏,由于大多数编译体系在宏打开之后才会报错,这样会添加排错的难度。
8、尽量少用全局变量,多用局部变量。由于全局变量是放在数据存储器中,界说一个全局变量,MCU 就少一个可以运用的数据存储器空间,假如界说了太多的全局变量,会导致编译器无满足的内存可以分配。而局部变量大多定坐落MCU 内部的寄存器中,在绝大多数MCU 中,运用寄存器操作速度比数据存储器快,指令也更多更灵敏,有利于生成质量更高的代码,而且局部变量所的占用的寄存器和数据存储器在不同的模块中可以重复运用。
9、设定适宜的编译程序选项
许多编译程序有几种不同的优化选项,在运用前应了解各优化选项的意义,然后选用最适宜的一种优化办法。一般情况下一旦选用最高档优化,编译程序会近乎病态地寻求代码优化,可能会影响程序的正确性,导致程序运转犯错。因而应了解所运用的编译器,应知道哪些参数在优化时会受到影响,哪些参数不会受到影响。
在ICCAVR 中,有“Default”和“Enable Code Compression”两个优化选项。
在CodeVisionAVR 中,“Tiny”和“small”两种内存方法。
在IAR ==有7 种不同的内存方法选项。
在GCCAVR 中优化选项更多,一不小心更简单选到不恰当的选项。
二、代码的优化
1、挑选适宜的算法和数据结构
应该了解算法言语,知道各种算法的优缺点,详细材料请拜见相应的参考材料,有许多核算机书本上都有介绍。将比较慢的次序查找法用较快的二分查找或乱序查找法替代,刺进排序或冒泡排序法用快速排序、兼并排序或根排序替代,都可以大大提高程序履行的功率。.挑选一种适宜的数据结构也很重要,比方你在一堆随机寄存的数中运用了许多的刺进和删去指令,那运用链表要快得多。
数组与指针具有非常暗码的联系,一般来说,指针比较灵敏简练,而数组则比较直观,简单了解。关于大部分的编译器,运用指针比运用数组生成的代码更短,履行功率更高。但是在Keil 中则相反,运用数组比运用的指针生成的代码更短。
2、运用尽量小的数据类型
可以运用字符型(char)界说的变量,就不要运用整型(int)变量来界说;可以运用整型变量界说的变量就不要用长整型(long int),能不运用浮点型(float)变量就不要运用浮点型变量。当然,在界说变量后不要超越变量的作用规模,假如超越变量的规模赋值,C 编译器并不报错,但程序运转成果却错了,而且这样的过错很难发现。在ICCAVR 中,可以在Options 中设定运用printf 参数,尽量运用根本型参数(%c、%d、%x、%X、%u 和%s 格局阐明符),少用长整型参数(%ld、%lu、%lx 和%lX 格局阐明符),至于浮点型的参数(%f)则尽量不要运用,其它C 编译器也相同。在其它条件不变的情况下,运用%f 参数,会使生成的代码的数量添加许多,履行速度下降。
3、运用自加、自减指令
一般运用自加、自减指令和复合赋值表达式(如a-=1 及a+=1 等)都可以生成高质量的程序代码,编译器一般都可以生成inc 和dec 之类的指令,而运用a=a+1 或a=a-1 之类的指令,有许多C 编译器都会生成二到三个字节的指令。在AVR 单片适用的ICCAVR、GCCAVR、IAR 等C 编译器以上几种书写办法生成的代码是相同的,也可以生成高质量的inc 和dec 之类的的代码。
4、削减运算的强度
可以运用运算量小但功用相同的表达式替换本来杂乱的的表达式。如下:
(1)、求余运算。
a=a%8;
可以改为:
a=a&7;
阐明:位操作只需一个指令周期即可完结,而大部分的C 编译器的“%”运算均是调用子程序来完结,代码长、履行速度慢。一般,只需求是求2n 方的余数,均可运用位操作的办法来替代。
(2)、平方运算
a=pow(a,2.0);
可以改为:
a=a*a;
阐明:在有内置硬件乘法器的单片机中(如51 系列),乘法运算比求平方运算快得多,由于浮点数的求平方是经过调用子程序来完结的,在自带硬件乘法器的AVR 单片机中,如ATMega163 中,乘法运算只需2 个时钟周期就可以完结。既使是在没有内置硬件乘法器的AVR 单片机中,乘法运算的子程序比平方运算的子程序代码短,履行速度快。
假如是求3 次方,如:
a=pow(a,3.0);
更改为:
a=a*a*a;
则功率的改进更显着。
(3)、用移位完结乘除法运算
a=a*4;
b=b/4;
可以改为:
a=a<<2;
b=b>>2;
阐明:一般假如需求乘以或除以2n,都可以用移位的办法替代。在ICCAVR 中,假如乘以2n,都可以生成左移的代码,而乘以其它的整数或除以任何数,均调用乘除法子程序。用移位的办法得到代码比调用乘除法子程序生成的代码功率高。实践上,只需是乘以或除以一个整数,均可以用移位的办法得到成果,如:
a=a*9
可以改为:
a=(a<<3)+a
5、循环
(1)、循环语
关于一些不需求循环变量参与运算的使命可以把它们放到循环外面,这儿的使命包含表达式、函数的调用、指针运算、数组拜访等,应该将没有必要履行屡次的操作悉数调集在一起,放到一个init 的初始化程序中进行。
(2)、延时函数:
一般运用的延时函数均选用自加的办法:
voiddelay(void)
{
unsignedinti;
for(i=0;i<1000;i++);
}
将其改为自减延时函数:
voiddelay(void)
{
unsignedinti;
for(i=1000;i>0;i–);
}
两个函数的延时作用类似,但简直一切的C 编译对后一种函数生成的代码均比前一种代码少1~3 个字节,由于简直一切的MCU 均有为0 搬运的指令,选用后一种办法可以生成这类指令。
在运用while 循环时也相同,运用自减指令操控循环会比运用自加指令操控循环生成的代码更少1~3 个字母。
但是在循环中有经过循环变量“i”读写数组的指令时,运用预减循环时有可能使数组超界,要引起留意。
(3)while 循环和do…while 循环
用while 循环时有以下两种循环办法:
unsignedinti;
i=0;
while(i<1000)
{
i++;
//用户程序
}
或:
unsignedinti;
i=1000;
do
i–;
//用户程序
while(i>0);
在这两种循环中,运用do…while 循环编译后生成的代码的长度短于while 循环。
6、查表
在程序中一般不进行非常杂乱的运算,如浮点数的乘除及开方等,以及一些杂乱的数学模型的插补运算,对这些即耗费时刻又消费资源的运算,应尽量运用查表的办法,而且将数据表置于程序存储区。假如直接生成所需的表比较困难,也尽量在启动时先核算,然后在数据存储器中生成所需的表,后以在程序运转直接查表就可以了,削减了程序履行过程中重复核算的工作量。
7、其它
比方运用在线汇编及将字符串和一些常量保存在程序存储器中,均有利于优化。