通过一定的风格来编写C程序,可以帮助C编译器生成执行速度更快的ARM代码。
下面就是一些与性能相关的关键点:
1.对局部变量、函数参数和返回值要使用signed和unsigned int类型。
这样可以避免类型转换,而且可高效地使用ARM的32位数据操作指令。
2.最高效的循环体形式是减计数到零(counts down to zero)的do-while循环。
3.展开重要的循环来减少循环的开销。
4.不要依赖编译器来优化掉重复的存储器访问。
指针别名会阻止编译器的这种优化。
5.尽可能把函数参数的个数限制在4个以内。
如果函数参数都存放在寄存器内,那么函数调用就会快得多。
6.按元素尺寸从小到大排列的方法来安排结构体,特别是在thumb模式下编译。
7.不要使用位域,可以用掩码和逻辑操作来替代。
8.避免除法,可以用倒数的乘法来替代。
9.避免边界不对齐的数据。
如果数据有可能边界不对齐,那么就要使用char *指针类型来访问。
10.在C编译器中使用内嵌汇编可以利用到C编译器本来不支持的指令或优化。
一、 数据类型使用上的优化
1.局部变量
一个char类型的数据比int类型的数据占用更小的寄存器空间或者更小的ARM堆栈空间。
这两种设想对于ARM来说,都是错误的。
所有的ARM寄存器都是32位的,所有的堆栈入口至少是32位的。
当我们执行i++,要利用当i=255后,i++=0这个条件时,可以把它定义为char类型。
2.函数参数
尽管宽和窄的函数调用规则各有其优点,但char或short类型的函数参数和返回值都会产生额外的开销,导致性能的下降,并增加了代码尺寸。
所以,即使是传输一个8位的数据,函数参数和返回值使用int类型也会更有效。
总结:
1)对于存放在寄存器中的局部变量,除了8位或16位的算术模运算外,尽量不要使用char和short类型,而要使用有符号或无符号int类型。
除法运算时使用无符号数执行速度更快。
2)对于存放在主存储器中的数组和全局变量,在满足数据大小的前提下,应尽可能使用小尺寸的数据类型,这样可以节省存储空间。
ARMv4体系结构可以有效地装载和存储所有宽度的数据,并可以使用递增数组指针来有效地访问数组。
对于short类型数组,要避免使用数组基地址的偏移量,因为LDRH指令不支持偏移寻址。
3)通过读取数组或全局变量并赋给不同类型的局部变量时,或者把局部变量写入不同类型的数组或者全局变量时,要进行显式数据类型转换。
这种转换使编译器可以明确、快速地处理,把存储器中数据宽度比较窄的数据类型扩展,并赋给寄存器中较宽的类型。
4)由于隐式或者显式的数据类型转换通常会有额外的指令周期开销,所以在表达式中应尽量避免使用。
Load和store指令一般不会产生额外的转换开销,因为load和store指令是自动完成数据类型转换的。
5)对于函数参数和返回值应尽量避免使用char和short类型。
即使参数范围比较小,也应该使用int类型,以防止编译器做不必要的类型转换。
二、C循环结构
在ARM上,一个循环其实只要2条指令就足够了:
- 一条减法指令,进行循环减法计数,同时设置结果的条件标志;
- 一条条件分支指令。
uint counter1(uint count){return (++count%60);}转换成:uint counter2(uint count){if (++count >=60) count=0;return (count);}
十、浮点运算
大多数ARM处理器硬件上并不支持浮点运算。
这样在一个对价格敏感的嵌入式应用系统中,可节省空间和降低功耗。
除了硬件向量浮点累加器VFP和ARM7500FE上的浮点累加器FPA外,C编译器必须在软件上提供浮点支持。
十一、内联函数和内嵌汇编
高效地调用函数,使用内联函数可以完全去除函数调用的开销,另外许多编译器允许在C源程序中使用内嵌汇编。
使用包含汇编的内嵌函数,可以使编译器支持通常不能有效使用的ARM指令和优化方法。
内联函数和内嵌汇编最大的好处是,可以实现一些在C语言部分中通常难以完成的操作。
使用内联函数要比使用#define宏定义更好,因为后者不检查函数参数和返回值的类型。
-END-
推荐阅读
【01】ARM嵌入式常用开发工具及开发流程(插画版) 【02】ARM架构及ARM指令集、Thumb指令集你了解多少? 【03】ARM到底是一家什么样的公司? 【04】一招教你ARM嵌入式系统硬件怎么用? 【05】带你走进嵌入式ARM与MMU神秘的内部世界 免责声明:整理文章为传播相关技术,版权归原作者所有,如有侵权,请联系删除