嵌入式C语言程序设计中的代码优化

时间:2022-10-21 01:39:34

嵌入式C语言程序设计中的代码优化

摘要:针对嵌入式系统的特点,介绍了几种在嵌入式系统程序设计中优化C语言代码的方法,从而提高系统的性能。

关键词:嵌入式系统;C语言;代码优化

中图分类号:TP311文献标识码:A文章编号:1009-3044(2010)01-126-02

The Code Optimization in Embedded Programing with C Language

WANG Zhi-chun

(Department of Computer, Hexi University, Zhangye 734000, China)

Abstract: According to the characteristics of embedded system, the paper introduced ways of c language code optimization in embedded system design so as to enhance the performance of the system.

Key words: embedded system; c language; code optimization

嵌入式系统是软件和硬件的紧密结合,具有软件代码小、高度自动化、响应速度快等特点。在嵌入式系统中开发高效率的程序涉及很多方面,如编程风格、算法设计、目标优化等,尤其是嵌入式高级语言的编程要结合软硬件开发环境,使得其软件受时间和空间的严格限制,加上运行环境复杂,使得嵌入式系统软件的开发变得异常困难。嵌入式系统C语言编程中适当的使用相关技巧,可以优化代码,提高程序的执行效率。

1 变量定义

在声明变量时,把相同类型的变量放在一起定义,可以优化内存。如下列定义的4个变量形式一样,次序不同确导致了不同的数据布局。如图1所示,显然第二种方式节约了更多的储存空间。

对于局部变量的定义,一般情况下使用short或char定义变量,可以节省空间;但局部变量数目有限时,结果相反。因为编译器会把局部变量分配给内部寄存器,每个变量占用一个寄存器。如图2所示,32位int变量最快,只用一条加法指令,而8位和16位变量,完成加法后,还需要在32位的寄存器中进行加法扩展。所以使用32位int或unsigned int局部变量最有效率。

2 适当的使用宏

在嵌入式系统中,当函数较短而传递参数较多的情况下,为了能达到性能要求,宏是一种很好的代替函数的方法。在C语言中宏是产生内嵌代码的唯一方法,是实现类似函数功能而又不具函数调用和返回开销的较好方法,但宏在本质上不是函数,因而要防止宏展开后出现不可预料的结果,对宏的定义和使用要慎而处之。

由于宏只是简单的替换,宏的参数如果是复合结构,那么通过替换之后可能由于各个参数之间的操作符优先级高于单个参数内部各部分之间相互作用的操作符优先级,如果我们不用括号保护各个宏参数,可能会产生预想不到的情形。比如:

#define ceil_div(x, y) (x + y - 1) / y

a = ceil_div( b & c, sizeof(int) );

将被转化为:

a = ( b & c + sizeof(int) - 1) / sizeof(int);

// 由于+/-的优先级高于&的优先级,那么上面式子等同于:

a = ( b & (c + sizeof(int) - 1)) / sizeof(int);

这显然不是调用者的初衷。为了避免这种情况发生,应当多写几个括号:

#define ceil_div(x, y) (((x) + (y) - 1) / (y))

比如:宏在展开的时候对其参数可能进行多次取值,但是如果这个宏参数是一个函数,那么就有可能被调用多次从而达到不一致的结果,甚至会发生更严重的错误。比如:

#define min(X,Y) ((X) > (Y) ? (Y) : (X))

c = min(a,foo(b));

这时foo()函数就被调用了两次。为了解决这个潜在的问题,我们应当这样写min(X,Y)这个宏:

#define min(X,Y) ({

typeof (X) x_ = (X);

typeof (Y) y_ = (Y);

(x_ < y_) ? x_ : y_; })

({...})的作用是将内部的几条语句中最后一条的值返回,它也允许在内部声明变量(因为它通过大括号组成了一个局部范围)。

3 循环条件

计数循环是程序中常用的控制结构,在C语言中经常用累加计数或递减计数的循环形式,这两种循环形式在逻辑上并没有效率差异,但映射到具体的体系结构中时,就产生了很大的不同。如图3所示,累加计数比递减计数多用一条指令。因为进行一个非0常数比较时,需用CMP指令执行;而当一个变量与0比较时,ARM指令可直接利用条件执行的特性(NE)来判别,所以当循环次数较大时,在性能上就会产生明显的差异。因此尽量使用递减计数设置循环条件。

4 活用位操作

使用C语言的位操作可以减少除法和取模的运算。在计算机程序中数据的位是可以操作的最小数据单位,理论上可以用"位运算"来完成所有的运算和操作,因而,灵活的位操作可以有效地提高程序运行的效率。比如:

对于以2的指数次方为"*"、"/"或"%"因子的数学运算,转化为移位运算">"通常可以提高算法效率。因为乘除运算指令周期通常比移位运算大。

C语言位运算除了可以提高运算效率外,在嵌入式系统的编程中,它的另一个最典型的应用是使用位间的与(&)、或(|)、非(~)操作对硬件寄存器进行位设置。

5 内嵌汇编

程序中对时间要求苛刻的部分可以用内嵌汇编处理,即在C程序中直接插入_asm{ }内嵌汇编语句,以带来速度上的显著提高。但是,开发和测试汇编代码是一件辛苦的工作,它将花费更长的时间,因而要慎重选择要用汇编的部分。

C程序能够与汇编程序实现方便灵活的接口,在C程序中调用汇编十分方便灵活,对二者调用的主要难度在于:实现数据的准确传输。为了使单独编译的C语言程序和汇编语言程序能相互调用,定义了统一的函数过程调用标准ATPCS(ARM-Thumb Procedure Call Standard)。ATPCS定义了寄存器组中的R0~R3作为参数传递和结果返回寄存器。如果数目超过4个,则使用堆栈传递。因为内部寄存器的访问速度远远大于存储器,所以尽量使参数传递在寄存器中进行,即尽量把函数的参数控制在4个以下。

6 利用硬件特性

由于CPU对各种存储器的访问速度是不同的,其速度基本上是:

CPU内部RAM > 外部同步RAM > 外部异步RAM> FLASH/ROM

对于程序代码,已经被烧录在FLASH或ROM中,我们最好在系统启动后将FLASH或ROM中的目标代码拷贝入RAM中后再执行以提高取指令速度;对于UART等设备,其内部有一定容量的接收BUFFER,我们应尽量在BUFFER被占满后再向CPU提出中断,从而避免浪费中断处理时间;如果对某设备能采取DMA方式读取,就采用DMA读取,DMA读取方式在读取目标中包含的存储信息较大时效率较高,其数据传输的基本单位是块,而所传输的数据是从设备直接送入内存的(或者相反)。DMA方式较之中断驱动方式,减少了CPU 对外设的干预,进一步提高了CPU与外设的并行操作程度。

7 结束语

总的来说,代码的优化和编译器、硬件结构有关。在使用这些技术提高代码运行速度的同时,相应的也会产生一些负面的影响,比如增加代码的大小、降低程序可读性等。因此在进行代码优化前要进行系统性能分析和评估,根据系统的实际需要,合理地运用上面介绍的一种或多种技术优化代码,提高代码的效率,以达到最佳的优化效果。

参考文献:

[1] 田泽.嵌入式系统开发与应用教程[M].北京:北京航空航天大学出版社,2005.

[2] 田晓梅,王月姣.嵌入式C语言在工程编程中的应用技巧[J].中南民族大学学报:自然科学版,2005(1).

[3] 吴俊军,刘东升.S3FC9DC单片机代码优化技术研究[J].微计算机信息,2007(8).

[4] 李石晶.关于嵌入式系统代码优化的一些体会[J].中国新通信,2007(11).

[5] 刘晓升.使用C语言编写高效嵌入式软件的教学探讨[J].计算机教育,2008(18).

上一篇:基层OA系统研究 下一篇:淮安广电机房信号质量监测管理信息化系统开发