《C语言代码优化.pdf》由会员分享,可在线阅读,更多相关《C语言代码优化.pdf(6页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、C 代码优化1.重要的 80-20 规则不要忘记由经验确定的80-20 规则,它宣称一个典型的程序用80%的时间执行20%的代码。这是一个重要的规则,因为它提醒你作为一个软件开发者的目标是识别出能全面提升你的程序性能的20%的代码。你可以用各种方式无限期地优化你的函数,但除非你将精力集中在正确的函数上,否则就是白白浪费精力。2.用指针运算代替数组索引这样做常常能产生又快又短的代码。与数组索引相比,指针一般能使代码速度更快,占用空间更少。使用多维数组时差异更明显。下面的代码作用是相同的,但是效率不一样。1.数组索引2.for(;)3.a=arrayt+;4.5.1.指针运算2.p=array;3
2、.for(;)4.a=*(p+);5.6.指针方法的优点是,array 的地址每次装入地址p 后,在每次循环中只需对p 增量操作。在数组索引方法中,每次循环中都必须根据t 值求数组下标的复杂运算。3.查表(游戏程序员必修课)一个聪明的游戏大虾,基本上不会在自己的主循环里搞什么运算工作,绝对是先计算好了,再到循环里查表。看下面的例子:旧代码:1.long factorial(int i)2.3.if(i=0)4.return 1;5.else 6.return i*factorial(i-1);7.新代码:1.static long factorial_table=1,1,2,6,24,120,
3、720 /*etc*/;2.long factorial(int i)3.4.return factorial_tablei;5.如果表很大,不好写,就写一个init 函数,在循环外临时生成表格。4.结构体成员的布局把长的类型放在短的前面,可以避免内存的空洞.5.提升循环的性能要提升循环的性能,减少多余的常量计算非常有用(比如,不随循环变化的计算)。6.Inline函数在 C+中,关键字 Inline 可以被加入到任何函数的声明中。这个关键字请求编译器用函数内部的代码替换所有对于指出的函数的调用。这样做在两个方面快于函数调用:第一,省去了调用指令需要的执行时间;第二,省去了传递变元和传递过程需
4、要的时间。但是使用这种方法在优化程序速度的同时,程序长度变大了,因此需要更多的ROM。使用这种优化在Inline函数频繁调用并且只包含几行代码的时候是最有效的。7.减少函数调用参数使用全局变量比函数传递参数更加有效率。这样做去除了函数调用参数入栈和函数完成后参数出栈所需要的时间。然而决定使用全局变量会影响程序的模块化和重入,故要慎重使用。8.static-把本地函数声明为静态的如果一个函数只在实现它的文件中被使用,把它声明为静态的(static)以强制使用内部连接。否则,默认的情况下会把函数定义为外部连接。这样可能会影响某些编译器的优化 比如,自动内联。9.参数、返回值、局部变量尽量使用32b
5、it 数据类型;因为多数ARM 处理器的操作是32bit 的,8bit 或 16bit 回增加操作的指令数(代码 1),并且这样可以保证边界对齐;参数不要超过4 个,返回值尽量是32bit 数据类型,因为ARM 遵循 ATPCS 要求,前四个参数使用寄存器(r0-r3)传递,返回值用r0 传递,多余的参数用栈传递;局部变量不要超过12 个,ARM 编译器通常将全局变量定位在存储空间中,局部变量分配给通用寄存器,ARM 中可通用的寄存器是12 个;1.intwordinc(inta)word_inc 2.ADD a1,a1,#1 3.return a+1;MOV pc,lr 4.5.shorts
6、hortinc(shorta)short_inc 6.ADD a1,a1,#1 7.return a+1;MOV a1,a1,LSL#16 8.MOV a1,a1,ASR#16 9.MOV pc,lr 10.11.charcharinc(chara)char_inc 12.ADD a1,a1,#1 13.return a+1;AND a1,a1,#&ff 14.MOV pc,lr 15.可以看出,操作 3 2 位变量所需的指令要少于操作8 位及 16 位变量。10.除法ARM 指令集中没有提供整数的除法,除法是由C 语言函数库中的代码(符号型 _rt_sdiv 和无符号型的 _rt_udiv)
7、实现的。一个32 位数的除法需要20 140 个周期。尽量以乘代除,配合移位运算。11.指针别名用一个局部变量来保存公共子表达式的值,保证该表达式只求一次值。建立一个新的局部变量来保存包含存储器访问的表达式,这样可以保证只对这个表达式求一次值。例如下面代码,前面的代码比后面的好。1.int a=datan;b+=a;c+=a;2.b+=datan;c+=datan;12.条件执行、循环终止的条件由于 ARM 指令可条件执行,所以充分利用cpsr 会使程序更有效率。在一个循环结构中,循环的终止条件将严重影响着循环的效率,再加上 ARM 指令的条件执行特性,所以在书写循环的终止条件时应尽量使用co
8、unt-down-to-zero结构。这样编译器可以用一条BNE(若非零则跳转)指令代替CMP(比较)和BLE(若小于则跳转)两条指令,既减小代码尺寸,又加快了运行ARM 速度。条件判断尽量与0 比较,理由同上。13.循环结构采用减计数循环比增计数循环更好,终止条件尽量写i!=0;循环变量起始值是变量且不等于0 的情况下用do-while 循环更优(终止条件在后);若循环体过于简单,比如少于4 个周期,可展开循环体(重复写几遍循环体代码),以免循环体代码还不如循环本身执行周期长,减少跳转指令打断流水线;尽量限制函数内部循环所用局部变量的数据,最多不要超过12 个,这样编译器就可以把他们都分配给
9、ARM 寄存器(变量个数多于寄存器个数时,在未被载入寄存器的变量需要运算时,需要找一个暂时不使用的寄存器R,载入变量,而在R 中原来保存的变量需要再次运算时,需要重新查找不用的寄存器,重新载入;即当变量个数多于寄存器个数时,一个变量的使用可能会经过多次查找寄存器,多次载入寄存器,多次被替换的过程);14.保持流水线畅通流水线阻断的情况可通过循环拆解等方法加以改善。一个循环可以考虑拆解以减小跳转指令在循环指令中所占的比重,进而提高代码效率。下面以一个内存复制函数加以ARM 说明。1.void memcopy(char *to,char *from,unsigned int nbytes)2.3.
10、while(nbytes-)ARM 4.*to+=*from+;5.为简单起见,这里假设nbytes 为 16 的 ARM 倍数(省略对余数的处理)。上面的函数每处理一个字节就要进行一次判断和跳转,对其中的循环体可作如下拆解:1.void memcopy(char *to,char *from,unsigned int nbytes)2.3.while(nbytes)4.*to+=*from+;5.*to+=*from+;6.*to+=*from+;7.*to+=*from+;8.nbytes-=4;9.10.这样一来,循环体中的指令数增加了,循环次数却减少了。跳转指令ARM 带来的负面影响得
11、以削弱。利用ARM 7 处理器 32 位字长的特性,上述代码可进一步作如下调整:1.void memcopy(char *to,char *from,unsigned int nbytes)2.3.int *p_to=(int *)to;4.int *p_from=(int *)from;5.while(nbytes)6.*p_to+=*p_from+;7.*p_to+=*p_from+;8.*p_to+=*p_from+;9.*p_to+=*p_from+;10.nbytes-=16;11.12.经过优化后,一次循环可以处理16 个字节。跳转指令带来的影响ARM 进一步得到减弱。不过可以看出,调整后的代码在代码量方面有所增加。15.充分利用片内RAM 一些厂商出产的ARM 芯片内集成有一定容量的RAM,如 Atmel 公司的 AT91R40807 内有 128KB 的 RAM,夏普公司的LH75400/LH75401 内有 32KB 的 RAM。处理器对片内RAM 的访问速度要快于对外部RAM 的访问,所以应尽可能将程序调入片内RAM 中运行。若因程序太大无法完全放入片内RAM,可考虑ARM 将使用 最频繁的数据或程序段 调入片内RAM 以提高程序运行效率。16.参考http:/ http:/ http:/
限制150内