首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

ARM下高效C编程更新介绍

ARM下高效C编程更新介绍


寄存器分配

高效的寄存器分配

应该尽量限制函数内部循环所用局部变量的数目,最多不超过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 使用整型逻辑运算ANDOR异或操作和屏蔽对位域进行测试、取反和设置操作。这些操作编译效率高,还可以同时对多个位域进行测试、取反和设置。

八、
边界不对齐数据和字节排列方式(大/小端)

边界不对齐数据和字节排列方式这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。可用乘以s2k位乘法来代替除以dk位无符号整数除法。

4)使用2的整数次幂作除数。当2的整数次幂做除数时,编译器会自动将除法运算转换成移位运算。所以在编写程序算法时,尽量使用2的整数次幂做除数。

5)求余运算。可以将一些典型的求余运算进行转换,以避免在程序中使用除法运算。如:

uint counter1(uint count)

{

        return (++count%60);

}

转换成:

uint counter2(uint count)

{

        if (++count >=60)

        count=0;

        return (count);

}

十、
浮点运算

大多数ARM处理器硬件上并不支持浮点运算。这样在一个对价格敏感的嵌入式应用系统中,可节省空间和降低功耗。除了硬件向量浮点累加器VFPARM7500FE上的浮点累加器FPA外,C编译器必须在软件上提供浮点支持。

十一、内联函数和内嵌汇编

高效地调用函数,使用内联函数可以完全去除函数调用的开销,另外许多编译器允许在C源程序中使用内嵌汇编。使用包含汇编的内嵌函数,可以使编译器支持通常不能有效使用的ARM指令和优化方法。


内联函数和内嵌汇编最大的好处是,可以实现一些在C语言部分中通常难以完成的操作。使用内联函数要比使用#define宏定义更好,因为后者不检查函数参数和返回值的类型。

返回列表