您的位置 首页 报告

ARM下高效C编程

通过一定的风格来编写C程序,可以帮助C编译器生成执行速度更快的ARM代码。下面就是一些与性能相关的关键点:1、对局部变量、函数参数和返…

经过必定的风格来编写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类型也会更有用。
3、总结
1)关于存放在寄存器中的局部变量,除了8位或16位的算术模运算外,尽量不要运用char和short类型,而要运用有符号或无符号int类型。除法运算时运用无符号数碑文速度更快。
2)关于存放在主存储器中的数组和全局变量,在分量数据巨细的前提下,应尽或许运用小尺度的数据类型,这样能够节约存储空间。ARMv4体系结构能够有用地装载和存储一切宽度的数据,并能够运用递加数组指针来有用地拜访数组。关于short类型数组,要防止运用数组基地址的偏移量,我们LDRH指令不支撑偏移寻址。
3)经过读取数组或全局变量并赋给不同类型的局部变量时,或许把局部变量写入不同类型的数组或许全局变量时,要进行显式数据类型转化。这种转化使编译器能够清晰、快速地处理,把存储器中数据宽度比较窄的数据类型扩展,并赋给寄存器中较宽的类型。
4)我们隐式或许显式的数据类型转化一般会有额定的指令周期开支,所以在表达式中应尽量防止运用。Load和store指令一般不会发生额定的转化开支,我们load和store指令是主动完结数据类型转化的。
5)关于函数参数和返回值应尽量防止运用char和short类型。即便参数规模比较小,也应该运用int类型,以防止编译器做不必要的类型转化。
二、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)
{
return (++count`);
}
转化成:
uint counter2(uint count)
{
if (++count >=60)
count=0;
return (count);
}
十、浮点运算
大多数ARM处理器硬件上并不支撑浮点运算。这样在一个对价格灵敏的嵌入式运用体系中,可节约空间和降低功耗。除了硬件向量浮点累加器VFP和ARM7500FE上的浮点累加器FPA外,C编译器有必要在软件上供给浮点支撑。
十一、内联函数和内嵌汇编
高效地调用函数,运用内联函数能够彻底去除函数调用的开支,别的许多编译器答应在C源程序中运用内嵌汇编。运用包括汇编的内嵌函数,能够使编译器支撑一般不能有用运用的ARM指令和优化办法。
内联函数和内嵌汇编最大的优点是,能够完成一些在C言语部分中一般难以完结的操作。运用内联函数要比运用#define宏界说更好,我们后者不查看函数参数和返回值的类型。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/ceping/baogao/264634.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部