经过必定的风格来编写C程序,能够协助C编译器生成碑文速度更快的ARM代码。下面便是一些与功能相关的要害点:
一、 数据类型运用上的优化
1、局部变量
一个char类型的数据比int类型的数据占用更小的寄存器空间或许更小的ARM仓库空间。这两种想象关于ARM来说,都是过错的。一切的ARM寄存器都是32位的,一切的仓库进口至少是32位的。当咱们碑文i++,要运用当i=255后,i++=0这个条件时,能够把它界说为char类型。
二、C循环结构
在ARM上,一个循环其实只需2条指令就足够了:
一条减法指令,进行循环减法计数,一起设置成果的条件标志;
一条条件分支指令。
这儿的要害是,循环的停止条件应为减计数到零,而不是计数添加到某个特定的约束值。我们减计数结构已存储在条件标志里,与零比较的指令就能够省掉了。我们不必i作为数组的下标索引,选用减计数就没有任何问题了。
总而言之,不管关于有符号的循环计数值,都应运用i!=0作为循环的完毕条件。对有符号数i,这比运用条件i>0少了一条指令。
总结:
1) 运用减计数到零的循环结构,这样编译器就不需求分配一个寄存器来保存循环停止值,而且与0比较的指令也能够省掉。
2) 运用无符号的循环计数值,循环持续的条件为i!=0而不是i>0,这样能够确保循环开支只要两条指令。
3) 假如事前知道循环体至少会碑文一次,那么运用do-while循环要比for循环要好,这样能够使编译器省去查看循环计数值是否为零的过程。
4) 打开重要的循环体可下降循环开支,但不要过度打开,假如循环的开支对整个程序来说占的份额很小,那么循环打开反而会添加代码量并下降cache的功能。
5) 尽量使数组的巨细是4或8的倍数,这样能够简单的以2,4,8次等多种挑选打开循环,而不需求忧虑剩下数组元素的问题。
三、 寄存器分配
高效的寄存器分配
应该尽量约束函数内部循环所用局部变量的数目,最多不超越12个,这样,编译器就能够把这些变量都分配给ARM寄存器。
四、 函数调用
4寄存器规矩
带有4个或许更少参数的函数,要比多于4个参数的函数碑文功率高得多。对带有少于4个参数的函数来说,编译器能够用寄存器传递一切的参数;而关于多于4个参数的函数,函数调用者和被调用者有必要经过拜访仓库来传递一些参数。
假如函数体积很小,只用到很少的寄存器,那么还有一些其他的办法来削减函数调用的开支。能够把调用函数和被调用函数放在同一个C文件中,这样编译器就知道了被调用函数生成的代码,并以此对调用函数进行一些优化。
总结:
1) 尽量约束函数的参数,不要超越4个,这样函数调用的功率会更高。也能够将几个相关的参数组织在一个结构体中,用传递结构体指针来替代多个参数。
2) 把比较小的被调用函数和调用函数放在同一个源文件中,而且要先界说,后调用,编译器就能够优化函数调用或许内联较小的函数。
3) 对功能影响较大的重要函数可运用要害字_inline进行内联。
五、 指针别号
界说:当2个指针指向同一个地址目标时,这2个指针被称作该目标的别号(alias)。假如对其间一个指针进行写入,就会影响从另一个指针的读出。在一个函数中,编译器一般不知道哪一个指针是别号,哪一个不是;或哪一个指针有别号,哪一个没有。
防止指针别号:
1) 不要依靠编译器来消除包括存储器拜访的公共子表达式,而应树立一个新的局部变量来保存这个表达式的值,这样能够确保只对这个表达式求一次值;
2) 防止运用局部变量的地址,不然对这个变量的拜访功率会比较低。
六、 结构体组织
在ARM上运用结构体有2个问题需求考虑:结构体地址鸿沟对齐和结构体总的巨细。
取得高效结构体的准则:
1) 把一切8位巨细的元素组织在结构体的前面;
2) 以此组织16位、32位和64位的元素;
3) 把一切数组和比较大的元素组织在结构体最终。
4) 关于一条指令,假如结构体太大而不能拜访一切的元素,那么把元素组织到一个子结构体中。编译器能够和谐独自的子结构体的指针。
小结:
结构体元素要依照元素的巨细来摆放,以最小的元素放在开端,最大的元素组织在最终;防止运用很大的结构体,能够用层次化的小结构体来替代;为了进步可移植性,人工对API的结构体添加填充位,这样,结构体的组织将不会依靠与编译器;在API的结构体中要慎重运用枚举类型。一个枚举类型的巨细是编译器相关的。
七、 位域
注意事项:
1) 应防止运用位域,而运用#define或许enum来界说屏蔽位;
2) 运用整型逻辑运算AND、OR、“异或”操作和屏蔽对位域进行测验、取反和设置操作。这些操作编译功率高,还能够一起对多个位域进行测验、取反和设置。
八、 鸿沟不对齐数据和字节摆放办法(大/小端)
鸿沟不对齐数据和字节摆放办法这2个问题,可使内存拜访和移植问题复杂化。须考虑数组指针是否鸿沟对齐,ARM装备是大端(big-endian),仍是小端(little-endian)的存储器体系。
总结:
1) 尽量防止运用鸿沟不对齐的数据;
2) 运用类型char *可指向恣意字节鸿沟的数据。经过读字节来拜访数据,运用逻辑操作来组合数据,这样代码就不会依靠于鸿沟是否对齐或许ARM的字节摆放办法的装备;
3) 为了快速拜访鸿沟不对齐的结构体,能够依据指针鸿沟和处理器的字节排序办法写出不同的程序变体。
九、除法
ARM硬件上不支撑除法指令,当代码中呈现除法运算时,ARM编译器会调用C库函数(有符号的除法调用_rt_sdiv,无符号的调用_rt_udiv),来完成除法操作。有许多不同类型的除法程序来习惯不同的除数和被除数。
总结:
1) 尽或许防止运用除法。对环形缓冲区的处理能够不必除法。
2) 假如不能防止除法运算,那么尽或许考虑运用除法程序一起发生商n/d和余数n%d的优点。
3) 关于重复对同一除数d的除法,预先计算好s=(2k-1)/d。可用乘以s的2k位乘法来替代除以d的k位无符号整数除法。
4)运用2的整数次幂作除数。当2的整数次幂做除数时,编译器会主动将除法运算转化成移位运算。所以在编写程序算法时,尽量运用2的整数次幂做除数。
5)求余运算。能够将一些典型的求余运算进行转化,以防止在程序中运用除法运算。如:
uint counter1(uint count)
{
}
转化成:
uint counter2(uint count)
{
}
十、 浮点运算
大多数ARM处理器硬件上并不支撑浮点运算。这样在一个对价格灵敏的嵌入式运用体系中,可节约空间和降低功耗。除了硬件向量浮点累加器VFP和ARM7500FE上的浮点累加器FPA外,C编译器有必要在软件上供给浮点支撑。
十一、内联函数和内嵌汇编
高效地调用函数,运用内联函数能够彻底去除函数调用的开支,别的许多编译器答应在C源程序中运用内嵌汇编。运用包括汇编的内嵌函数,能够使编译器支撑一般不能有用运用的ARM指令和优化办法。