程序可移植性设计及性能优化

来源:网络收集 时间:2025-06-15 下载这篇文档 手机版
说明:文章内容仅供预览,部分内容可能不全,需要完整文档或者需要复制内容,请下载word后使用。下载word有问题请添加微信号:xuecool-com或QQ:370150219 处理(尽可能给您提供完整文档),感谢您的支持与谅解。点击这里给我发消息

a) 关键字const 的作用是为给读你代码的人传达非常有用的信息。例如,在函数的形参前添加const 关键字意味着这个参数在函数体内不会被修改,属于\输入参数\。在有多个形参的时候,函数的调用者可以凭借参数前是否有const 关键字,清晰的辨别哪些是输入参数,哪些是输出参数。

b) 合理地使用关键字const 可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改,这样可以将错误扼杀在摇篮中,减少bug 的出现,提高了软件的可靠性。

c) 对于输入参数,不能将const实参传递给非const形参,因为函数无法保证在内部不改变传进来的实参值;但若声明形参为const类型的,则其可以接受任意类型参数。所以为了减少对函数调用时实参的限制,将输入参数声明为const类型是最合适的方式。

采用上面的规则设计几个函数,如下:

SYS_STATUS DHPI_Tx(u32 u32SrcId, u32 u32DstId, u16 u16LinkId, u32 u32Len, const void *pvoidData)

当你只引用pvoidData所指向的值时应该加上const修饰符,程序中若修改了编译就不能通过,可以减少程序的bug。

void DD_EdmaReconfig(const u32 * const pu32Src, const u32 * const pu32Dst, u32 u32Cnt, u32 u32EdmaChan)

pu32Src和pu32Dst都是输入参数,同时pu32Src和pu32Ds本身不可修改,这就保证了只能读取固定位置的值。

若如此声明

void DD_EdmaReconfig(u32 * const pu32Src, u32 * const pu32Dst, u32 u32Cnt, u32 u32EdmaChan)

上述形式函数参数则可能为输出参数,只能读取或者更改pu32Src和pu32Ds位置的值。

本文来自CSDN博客,转载请标明出处:

http://blog.csdn.net/sailor_8318/archive/2008/07/20/2679385.aspx

嵌入式系统程序可移植性设计及性能优化之四

――程序设计

Sailor_forever sailing_9806@163.com 转载请注明

http://blog.csdn.net/sailor_8318/archive/2008/07/29/2735041.aspx

【摘要】本节介绍了嵌入式系统程序设计中如何提高运行性能。通常程序的代码量和运行速度是相互制约的,性能优化需要综合考虑这两个因素。本章所述的大部分技巧对于一般的程序不会有太大的影响,但对于嵌入式系统这种对实时性有一定要求的情况下,可以考虑本文的相关技巧。现在的编译器都可以设置优化选项,一般O2优化就可以实现本文所提到的相关优化技巧,但尽量避免不同的优化选项造成的差异性,手动优化可以保证程序按照自己的意图运行。

【关键词】嵌入式,可移植性,程序设计,运行性能,实时性,优化级别

4 程序设计... - 22 -

4.1 循环转置... - 22 -

4.2 减小运算强度... - 23 -

4.2.1 位操作实现求余运算... - 23 -

4.2.2 用移位实现乘除法运算... - 23 -

4.2.3 将循环体内的乘法运算改成循环自加运算... - 23 -

4.3 减少不变计算... - 24 -

4.3.1 循环内部避免恒定式... - 24 -

4.3.2 避免结构体深度访问... - 25 -

4.4 减少存储访问指令周期和个数... - 26 -

4.5 查表... - 28 -

4.6 使用自加、自减指令... - 28 -

4.7 根据频率进行case 排序... - 29 -

4.8 函数指针表替代switch-case. - 30 -

1 程序设计 1.1 循环转置

通常使用的延时函数均采用A中自加的形式,而B中为自减延时函数

A 自加 B 自减

void delay (void) {

u32 i;

for (i=0;i<1000;i++) nop; }

void delay (void) {

u32 i;

for (i=1000;--i;)

nop; }

两个函数的延时效果相似,但几乎所有的C编译对后一种函数生成的代码均比前一种代码少1~3个指令,因为几乎所有的MCU均有为0转移(JNZ)的指令,采用后一种方式能够生成这类指令。

在使用while循环时也一样,使用自减指令控制循环会比使用自加指令控制循环生成的代码更高效。如果循环对方向不敏感,即循环变量只是控制循环的次数,循环体和循环变量无关,可以由大向小循环。但是在循环中有通过循环变量“i”读写数组的指令时,使用预减循环时有可能使数组超界。当然你可以通过对i做加减运算来纠正,但是这样加速的作用就没有了。

1.2 减小运算强度

数据的位是可以操作的最小数据单位,一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。可以使用运算量小但功能相同的表达式替换原来复杂的的表达式。

1.2.1 位操作实现求余运算 A求余运算 B 位操作

a=a%8; a &= 7;

位操作只需一个指令周期即可完成,而大部分的C编译器的“%”运算均是运用除法指令来完成,代码长、执行速度慢。只要是求2^n的余数,均可使用位操作的方法来代替。但其可读性不好,所实现的功能不直观,若在循环内部进行大量此类操作时,位操作的优势将体现

出来。

1.2.2 用移位实现乘除法运算

通常如果需要乘以或除以2^n,都可以用移位的方法代替。实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果。

A 乘除法

#define DD_WORD2BYTE(word) ((word) * 4)

#define DD_BYTE2WORD(byte) (((byte) + 3) / 4)

a=a*9 B 移位

#define DD_WORD2BYTE(word) ((word) << 2)

#define DD_BYTE2WORD(byte) (((byte) + 3) >> 2)

a=(a<<3)+a

1.2.3 将循环体内的乘法运算改成循环自加运算

A 乘法

B 循环自加

for (i = 0; i < MAX; i++) {

h = 14 * i;

嵌入式系统程序可移植性设计及性能优化之一 ――――宏定义设计

Sailor_forever sailing_9806@163.com 转载请注明

http://blog.csdn.net/sailor_8318/archive/2008/07/16/2663254.aspx

【摘要】本节介绍了嵌入式系统程序设计中采用宏定义进行常量定义的必要性。说明了宏常量定义的基本规则以及如何采用依赖关系定义宏常量来保证其可移植性和裁减性。最后介绍了如何利用宏定义实现掩码偏移量等来高效的进行位操作。

【关键词】嵌入式,可移植性,宏定义,依赖关系,掩码,偏移量,位操作

1 宏定义设计... - 1 -

1.1 为何要采用宏定义?... - 1 -

1.2 宏定义的基本规则... - 1 -

1.3 依赖关系定义宏改善移植性... - 1 -

1.4 通过偏移量和掩码进行位操作... - 2 -

1 宏定义设计

1.1 为何要采用宏定义?

在程序设计过程中,对于经常使用的一些常量,如果将它直接写到程序中去,一旦常量的数值发生变化,就必须逐个找出程序中所有的常量,并逐一进行修改,这样必然会降低程序的

可维护性。因此,应尽量通过预处理命令方式将常量定义为特定的字符,这样常量就有了统一的表现形式,不会出现输入错误导致的不一致性。另外宏常量意义明确,大大改善了代码的可读性。

只读的变量也可以实现上述宏常量所带来的可移植性、可靠性及可读性等特点,但其要占据存储空间,需要访问内存,相比宏常量的立即数寻址而言效率要低。在C++中提倡用const只读变量来定义常量,是因为这样可以提供更严格的类型安全检查。但由于C中const只读变量不能用于某些场合,因此在嵌入式C中仍多数采用宏来定义常量。

1.2 宏定义的基本规则

下面以一个实例来说明宏定义的基本规则,如用预处理指令#define 声明一个常量,用以表明1年中有多少秒,不考虑润年

#define C_SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

a) 命名风格,为了与普通变量区分开来,宏定义通常全部大写,多个单元之间用下划线隔开;

b) 整个表达式应括起来,若有参数则应将每个参数都括起来,防止替换扩展后可能带来的异常问题;

c) 常量表达式先合并后再替换。预处理器将为你计算常量表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有运行性能代价的。

d) 为常量添加类型信息。宏的不足之一在于缺乏类型安全检查,人为的提供类型信息可以有效检查出此类问题。UL告诉编译器这个常量是无符号长整型数,因此将其赋值给u16型变量会出现告警。

1.3 依赖关系定义宏改善移植性

嵌入式系统程序的最大特点是硬件平台的多变性,因此需要根据具体的应用情况更改大量配置,而这些配置基本都是由宏定义来实现的,放在特定的头文件中,与其他的代码隔离,在一定程度上改善了代码的可移植性。但有些时候,多个宏定义有严重的依赖关系,增减某个宏会引起其他定义的更改,如何定义这些宏对嵌入式程序的可移植性有很大影响。 A

常量分别定义

#define C_DD_MODULE_ID_AOM (0x00010101) /* AOM模块ID */

#define C_DD_MODULE_ID_RRCM (0x00010102) /* RRCM模块ID */

#define C_DD_MODULE_ID_RLC (0x00010103) /* RLC模块ID */

#define C_DD_MODULE_ID_TRM (0x00010104) /* TRM模块ID */

#define C_DD_MODULE_ID_MCP_MIN (C_DD_MODULE_ID_AOM)

#define C_DD_MODULE_ID_MCP_MAX (C_DD_MODULE_ID_TRM) B

依赖定义

#define C_DD_MODULE_ID_MCP_MIN (0x00010101) /* MCP最小模块ID */

#defineC_DD_MODULE_ID_AOM (C_DD_MODULE_ID_MCP_MIN) /* AOM模块ID */

#define C_DD_MODULE_ID_RRCM (C_DD_MODULE_ID_AOM + 1) /* RRCM模块ID */

#define C_DD_MODULE_ID_RLC (C_DD_MODULE_ID_RRCM + 1) /* RLC模块ID */

#define C_DD_MODULE_ID_TRM (C_DD_MODULE_ID_RLC + 1) /* TRM模块ID */

#define C_DD_MODULE_ID_MCP_MAX (C_DD_MODULE_ID_TRM) /* MCP最大模块ID */

“A常量分别定义”方式,因为各个宏定义值必须连续,若更改或者删除C_DD_MODULE_ID_AOM,其他的定义基本都受到影响,将严重影响到代码的可扩充性和可裁减性。

“B依赖定义”方式,其原则是:

a) base用常量定义;

b) 第一个定义为base;

c) 其他的在上一个基础上加1;

d) max项即为最后一项。

这样整体改动起来只需要更改base;在中间删除或添加部分项时只需要更改一个上下衔接处即可;添加则比较简单,只需要在原有最后项后添加即可。

这种方式若改动部分定义对其他定义的影响最小,大大改善了代码的可移植性。

1.4 通过偏移量和掩码进行位操作 嵌入式系统经常需要和硬件打交道,而配置硬件寄存器则是系统初始化阶段的重要任务,如何清晰明了的进行配置决定了代码的可读性。尽管可以使用位域,但位域是不可移植的,因此利用宏定义来解决可移植性问题。

对于每一个待操作相应位来说,应具备以下几个标识:

待操作的位名称B_NAME;

操作位所占据的位宽B_WIDTH_NAME

操作位第一位的偏移量B_SHIFT_NAME

操作位的掩码B_MASK_NAME

以PRI为例进行说明:

#define PRI DD_C64_EDMA_OPT_PRI

#define C_WIDTH _DD_C64_EDMA_OPT_PRI (3)

#define C_SHIFT_DD_C64_EDMA_OPT_PRI (29)

可以手动定义对应位的掩码,如下:

#define C_MASK_DD_C64_EDMA_OPT_PRI (0xe0000000)

但更好的方式是利用位宽和偏移量来自动构成掩码

#define BIT_MASK(_name) (((1U<< C_WIDTH _##_name))-1)<<( C_SHIFT_##_name)

#define C_MASK_DD_C64_EDMA_OPT_PRI BIT_MASK(PRI)

BIT_MASK(PRI)经过宏替换后即为:

(((1U<<( C_WIDTH _DD_C64_EDMA_OPT_PRI))-1) \\

<<( C_SHIFT_DD_C64_EDMA_OPT_PRI)

对于每个位的取值也应该用宏定义来表示,这样清晰明确

#define C_DD_C64_EDMA_OPT_URGENT_PRI (0x0)

#define C_DD_C64_EDMA_OPT_HIGH_PRI (0x1)

#define C_DD_C64_EDMA_OPT_MEDIUM_PRI (0x2)

#define C_DD_C64_EDMA_OPT_LOW_PRI (0x3)

具备了掩码和偏移量即可对各个位进行操作了

#define SET_BITS(_reg,_name_val)\\

((_reg)=((_ reg)&~(BIT_MASK(_name))) \\

| (((_val)<<( C_SHIFT_##_ name))&(BIT_MASK(_name))))

通过如下方式调用设置优先级

Sentence3;

可以看出扩展后的结果和设计情况相差很远,最简单的解决情况是:任何if?else下面的语句都用“{}”括起来。这样可以保证在宏函数扩展后不会出现异常。

If(condition1) {

MacroA;

} else {

MacroB; }

If(condition1) {

if(conditon)

Sentence1;

} else {

sentence2;

Sentence3; }

但顾客是上帝,我们不能要求使用我们的宏函数的用户任何时候都用“{}”来构造if?else语句,我们要本着“严于律己,宽以待人”的原则来设计我们的宏函数,这样就可以保证无论用户是否操作规范,我们总能达到他们的要求。

可有以下两种方式:

1) Do?.while(0)结构

这是宏函数最常见的方式,如:

#define DD_INIT_ISR_TIME_STATS(pstruIsrTime) do \\

{ \\

(pstruIsrTime)->u32IsrStartTestFlag = 0; \\

(pstruIsrTime)->u32IsrMinGapTime = C_DD_WORD_VAL_MAX; \\

}while(0)

本质上“{}”来保证多条语句为一个执行序列的,那么下列方式可以么?

#define DD_INIT_ISR_TIME_STATS(pstruIsrTime) { \\

(pstruIsrTime)->u32IsrStartTestFlag = 0; \\

(pstruIsrTime)->u32IsrMinGapTime = C_DD_WORD_VAL_MAX; \\

}

看似可以,实则不然,因为宏函数的规则是调用时将添加“;”,那么上述宏函数扩展后即为

{

(pstruIsrTime)->u32IsrStartTestFlag = 0;

(pstruIsrTime)->u32IsrMinGapTime = C_DD_WORD_VAL_MAX; };

最后一个“;”就是多余的,不能编译通过。但单独一行的“;”是可以的,其表示空语句。

这也就是为什么上面添加了一条无用的while(0),起保证宏扩展后是正确的,同时不会对代码产生任何实质性的影响。

编译阶段编译器会对上述代码进行优化,while(0)语句实际上是不会执行的,因此上述Do?.while(0)结构是不会损失性能的。

2) 逗号表达式“,”

其可以保证多条语句可以作为一个整体执行。虽然是正确的,但扩展后不便于理解,因此使用较少。

1.3.3 宏函数的副作用

利用上述宏函数的基本规则求最小值的代码如下:

Temp = MIN(*p++, b);

宏函数只是将参数完全替换,即MIN(*p++, b),进行宏展开后为((*p++) <= (b) ? (*p++) : (b)),如果(*p++) <= (b)成立,则表达式的值为(*p++),但由于在(*p++)<= (b)判断过程中改变了p的值,使得此时的? (*p++)非(*p++)<= (b)中的值了,违背了?号表达式的原意。

尽管上述代码符合上面我们所说的宏参数及语句使用规则,扩展后仍然会出现问题,对于宏来说唯一的解决方案是避免宏参数为表达式,即改为:

x = *p++;

Temp = MIN(x, b);

这样带入的参数x即为一个确定的值,符合设计要求。

通常函数参数为表达式的唯一目的是为了支持链式表达,可以省去一个变量,除此之外并不会提高运行性能。同时对于参数入栈的顺序并没有统一的规定,这样某些编译器的差异会导致表达式参数值与设计的差异。因此应避免使用表达式参数。

从上面的阐述,可以看到宏有一些难以避免的问题,怎么解决呢?

内联函数是代码被插入到调用者代码处的函数。如同宏函数,内联函数通过避免被调用的开销来提高执行效率,尤其是它能够通过调用被编译器优化。

内联函数和宏函数很类似,而本质区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销,因此其执行速度比一般函数要快。你可以象调用函数一样来调用内联函数,而不必担心会产生于宏函数的一些问题。由上面的两者的特性可知,我们可以用内联函数完全取代预处理宏函数。

内联inline函数将进行参数检查,求出参数的值后再将此值带入函数中,因此((A) <= (B) ? (A) : (B))中的A是一致的。

声明内联函数看上去和普通函数非常相似:

extern void f(u32 i, u8 u8c);

当你定义一个内联函数时,在函数定义前加上 inline 关键字,并且将定义放入头文件:

inline void f(u32 i, u8 u8c) { // ...

}

内联函数inline声明必须是和函数体的定义放在一起,才有效。

inline u32 function(u32 i) {return i*i;}

这样我们才算定义了一个内联函数。

当然,内联函数也有一定的局限性。就是函数中的执行代码不能太多了,如果,内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。这样,内联函数就和普通函数执行效率一样了。另外对于不支持inline内联函数的编译器来说,宏函数就是唯一的减少函数调用的途径了。

1.4 Const修饰输入指针参数

在C 语言中,大多数人的概念是\意味着常量\,但实则不然,const仅意味着:\只能读的普通变量\,可以称其为\不能改变的变量\,尽管这个说法似乎很拗口,但却最准确的表达了C 语言中const 的本质。const是用来修饰变量的,在定义时赋初值,在编译阶段需要的常量仍然只能以#define 宏定义!

Const只是一个修饰符,不管怎么样a仍然是一个指向u32型数据的指针

a) const u32 *a; a是一个指向常整型数的指针,此最常见于函数参数,指针可修改,但其指向的内容不能修改。

b) u32 * const a; a是一个指向整型数的常指针,指针指向的整型数是可以修改的,但指针是不可修改的。

c) u32 const * a const; a是一个指向常整型数的常指针,指针指向的整型数是不可修改的,同时指针也是不可修改的。

上述定义方式的本质在于:const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效

也许你可能会问,即使不用关键字,也还是能很容易写出功能正确的程序,那么我们为什么还要如此看重关键字const呢?理由如下:

关代码无需任何修改,提高了代码的可移植性。

b) 设置发送或者接收的起始时间

#define DD_SET_TIME_STATS_START(pstruTxRxTime) (pstruTxRxTime)->u32Start = (*C_DD_C64_TIMER2_CNT)

乘以C_DD_C64_TIMER_CLK_DIVIDER后,就可能溢出了,因此u32Start只能以定时器timer个数为单位;因为“.”“->”优先级较高,因此应将pstruTxRxTime参数用括号扩起来,这也是宏参数的基本规则。

c) 获得发送或者接收的时间,并更新最大时间

#define DD_UPDATE_TIME_STATS(pstruTxRxTime) do{ \\

(pstruTxRxTime)->u32TimeGap = ((*C_DD_C64_TIMER2_CNT ) - (pstruTxRxTime)->u32Start) * C_DD_C64_TIMER_CLK_DIVIDER; \\

(pstruTxRxTime)->u32TimeGapMax = (((pstruTxRxTime)->u32TimeGap) > ((pstruTxRxTime)->u32TimeGapMax)) ? ((pstruTxRxTime)->u32TimeGap) : ((pstruTxRxTime)->u32TimeGapMax); \\

}while(0)

在咱们的代码中还有多处此类封装,如下:

将通道收发正确错误统计计数综合为一个数据结构,具有一定的共性,以供利用HPI 、DPRAM 及McBSP的模块使用。

/* HPI 、DPRAM 及McBSP 等通道收发统计变量数据结构 */

typedef struct tag_STRU_TX_RX_CNT_STATS {

u32 u32RxOkCnt; /* 接收正确的次数*/

u32 u32RxErrCnt; /* 接收错误的次数*/

u32 u32TxOkCnt; /* 发送正确的次数*/

u32 u32TxErrCnt; /* 发送错误的次数*/

} STRU_TX_RX_CNT_STATS;

/* 中断统计变量数据结构 */

typedef struct tag_STRU_ISR_TIME_STATS {

u32 u32IsrStartTestFlag; /* 开始测量中断时间标志,获取u32IsrPrevCounter*/

u32 u32IsrTotalCnt; /* 该中断发生的总次数*/

u32 u32IsrPrevCounter; /* 上次发生中断时timer2的计数值*/

u32 u32IsrCurGapTime; /* 两次中断的间隔时间,单位CPU cycle*/

u32 u32IsrMinGapTime; /* 两次中断的最小间隔时间,单位CPU cycle*/

u32 u32IsrMaxGapTime; /* 两次中断的最大间隔时间,单位CPU cycle*/

} STRU_ISR_TIME_STATS;

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/sailor_8318/archive/2008/07/17/2663633.aspx

嵌入式系统程序可移植性设计及性能优化之三

――――函数设计

Sailor_forever sailing_9806@163.com 转载请注明

http://blog.csdn.net/sailor_8318/archive/2008/07/20/2679385.aspx

【摘要】本章介绍了函数设计的一些基本规则。合理对各种参数进行封装,不但有利于模块的交互,更能够减少参数提高函数调用性能。其次介绍了模块划分的原则,如何减少模块间的耦合度。接着分析了宏函数中参数的基本规则、多宏语句的基本规则、宏和内联的区别以及如何防止宏参数宏语句扩展后的异常效应。最后介绍了如何利用const来进行输入参数的修饰及如何提高程序的可靠性。

【关键词】嵌入式,可移植性,函数设计,模块划分,耦合度,内聚性,内联,宏参数,const,输入参数,可靠性

3 函数设计... - 15 -

3.1 避免过多函数参数,提高调用性能... - 15 -

3.2 合理设计模块,减小耦合度... - 16 -

3.3 用宏函数提高时间效率... - 18 -

3.3.1 宏参数的基本规则... - 18 -

3.3.2 宏语句的基本规则... - 18 -

3.3.3 宏的副作用... - 20 -

3.4 Const修饰输入指针参数... - 21 -

1 函数设计

1.1 避免过多函数参数,提高调用性能

在函数设计时通常需要传递参数,为了提供函数调用的性能,某些处理器如ARM会利用寄存器来传递参数,这样不需要访问内存,其访问效率更高。但寄存器传递的参数数目有限,对于ARM体系是四个,当多于四个时,剩余的参数需要用栈传递,参数的出栈入栈都需要时间,调用性能下降。当参数之间紧密相连且通常需要在多个模块中联合使用时,应对参数进行封装,这样便于参数的传递和变量的管理。

如:SYS_STATUS DCCM_SetSfnTsn(u16 u16SfnPeriod, u16 u16TsnPeriod, u16 u16Sfn, u16 u16Tsn, u32 u32TodPart1, u32 u32TodPart2)

函数六个参数,对这些参数的解析在函数内部又要定义六个变量与之对应,这给变量的定义个管理带来了不便,如下:

u16 g_u16DCCMSfn;

u16 g_u16DCCMTsn;

u16 g_u16DCCMSfnPeriod;

u16 g_u16DCCMTsnPeriod;

STRU_DD_TOD_INFO g_struDCCMTod;

因此应将相关联的变量封装为结构体,优势在于:

a) 便于管理、定义、声明,避免零散的变量;

b) 意义明确,结构清晰;

c) 函数调用时避免传递过多参数,提高函数调用性能,参数少不易出错;

不足在于对于结构体的访问效率不如单独的变量,但此性能影响很小;为了代码更好的可读性、可移植可维护性性和可靠性,此处结构体的形式更合适。

MAT、CCM及MCP都需要用到此类信息,因此应单独提炼出结构体便于各模块的交互;模块间进行通信时要遵循一定的协议,即数据交互时要按照一定的数据格式进行传输,为防止各模块对格式的认识不统一,最好的方式是提供统一的数据结构的定义来进行数据收发

BPP端封装为STRU_BPP_TOD_SFN_TSN_PARAM

typedef struct tag_STRU_BPP_TOD_SFN_TSN_PARAM {

STRU_DD_TOD_INFO struTod;

u16 u16SfnPeriod;

u16 u16TsnPeriod;

u16 u16Sfn;

u16 u16Tsn;

} STRU_BPP_TOD_SFN_TSN_PARAM;

而MCP端对此结构的定义为

typedef struct tag_STRU_DD_MCPBPP_TODTSNSFN_INFO {

u32 u32TodPart1;

u32 u32TodPart2;

u16 u16SfnPrd;

u16 u16TsnPrd;

u16 u16SfnNum;

u16 u16TsnNum;

} STRU_DD_MCPBPP_TODTSNSFN_INFO;

可以发现,原来BPP和MCP的MAT模块对时隙、子帧、TOD信息的处理并没有采取统一

的方式,这样在交互时容易出现问题。并且二者分别定义,有重复劳动。

1.2 合理设计模块,减小耦合度

软件模块设计时需要合理划分模块的层次结构,提高内聚性,降低耦合度,引用全局变量会增强模块之间的耦合度。 如:

DCCM模块定义了g_u16DCCMTsn、g_u16DCCMSfn等全局变量,调用DBSP模块实现的时隙回调函数DBSP_TslotCb();

DBSP模块中DBSP_TslotCb()声明外部变量,通过全局变量引用g_u16DCCMTsn、g_u16DCCMSfn等变量,并且在DBSP_TslotCb()内部只是读取了g_u16DCCMTsn、g_u16DCCMSfn的值。

因此更改函数形式,传递参数,而非引用全局变量;不更改参数值,采用值传递

void DBSP_TslotCb (u16 u16Sfn, u16 u16Tsn)

若DBSP_TslotCb不是由定义g_u16DCCMTsn、g_u16DCCMSfn等变量的DCCM模块调用,则必须声明外部变量然后引用,这种耦合是无法避免的。

另一个典型例子是资源的申请和释放。在具备操作系统的嵌入式系统中,因为嵌入式系统的内存空间往往是十分有限的,定义过多的全局变量将导致系统内存大量减少,因为全局变量在程序的整个运行期间都占据着同块内存;另外栈空间也是有限的,若在函数内部定义大容量的数据结构时,很可能导致栈溢出。上述两种情况都需要动态内存申请释放来解决,但不经意的内存泄露会很快导致系统的崩溃。

所以一定要保证你的malloc 和free 成对出现,如果你写出这样的一段程序:

u8 * function(void)

{

u8 *p;

p = (u8 *)malloc(…);

if(p==NULL) ?;

? /* 一系列针对p 的操作 */

return p; }

在某处调用function() ,用完function 中动态申请的内存后将其free ,如下:

u8 *q = function(); ?

free(q);

上述代码明显是不合理的,因为违反了malloc 和free 成对出现的原则,即\谁申请,就由谁释放\原则。不满足这个原则,会导致代码的耦合度增大,因为用户在调用function 函数时需要知道其内部细节!

正确的做法是在调用处申请内存,并传入function 函数,如下:

u8 *p=malloc(…);

if(p==NULL)

?;

function(p); ?

free(p);

p=NULL;

而函数function 则接收参数p,如下:

void function(u8 *p) {

? /* 一系列针对p 的操作 */ }

另外一些公用处理模块,为了满足各种不同的调用需要,往往在内部采用了大量的if-then-else结构,这样很不好,判断语句如果太复杂,会消耗大量的时间的,应该尽量减少公用代码块的使用,避免控制耦合。

1.3 用宏函数提高时间效率

在嵌入(inline)操作符变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的实时性能,嵌入代码经常是必须的方法。但是使用这种方法在优化程序速度的同时,程序长度变大了,因此需要更多的ROM空间。使用这种优化在宏函数频繁调用并且只包含几行代码的时候,对提高运行效率是最有效的。

函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行。同时函数调用是要使用系统的栈来保存数据的。因此,函数调用要有一定的时间和空间方面的开销,于是将影响其效率。

而宏函数不存在这个问题,其只是在预处理的地方将预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,不需要额外时间方面的开销。所以调用一个宏比调用一个函数更有时间效率,在频繁调用同一个宏函数的时候,该现象尤其突出。

1.3.1 宏参数的基本规则

a) 在引用宏参数时必须括起来,如参数为A,则引用时(A);

b) 若是宏表达式,则整个表达式必须括起来,如((A)+3);

采用上述规则定义一个\标准\宏MIN ,这个宏输入两个参数并返回较小的一个,如下:

#define MIN(A,B) ((A)<= (B) ? (A) : (B))

参数和表达式都必须括起来是为了防止在宏参数替换后由于运算符优先级导致的问题,因为宏参数本身可以是表达式,而且当宏在表达式中展开时,你不知道表达式里还有没级别更高的运算

1.3.2 宏语句的基本规则

a) 对于多个语句一起构成的宏函数,必须保证在宏函数替换后其为一个完整的执行序列,即任何时候都是全部顺序执行完毕。

b) 宏语句的最后一句没有语句结束符“;”,而是在引用宏函数时添加。 如:

#define MacroA if(conditon); \\

Sentence1

#define MacroB sentence2; \\

Sentence3 If(condition1)

MacroA; Else

MacroB;

则宏函数替换扩展后,如下:

If(condition1)

if(conditon)

Sentence1; Else

sentence2;

Sentence3;

If(condition1) {

if(conditon)

Sentence1; else

sentence2; }

百度搜索“70edu”或“70教育网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,70教育网,提供经典综合文库程序可移植性设计及性能优化在线全文阅读。

程序可移植性设计及性能优化.doc 将本文的Word文档下载到电脑,方便复制、编辑、收藏和打印 下载失败或者文档不完整,请联系客服人员解决!
本文链接:https://www.70edu.com/wenku/184569.html(转载请注明文章来源)
Copyright © 2020-2025 70教育网 版权所有
声明 :本网站尊重并保护知识产权,根据《信息网络传播权保护条例》,如果我们转载的作品侵犯了您的权利,请在一个月内通知我们,我们会及时删除。
客服QQ:370150219 邮箱:370150219@qq.com
苏ICP备16052595号-17
Top
× 游客快捷下载通道(下载后可以自由复制和排版)
单篇付费下载
限时特价:7 元/份 原价:20元
VIP包月下载
特价:29 元/月 原价:99元
低至 0.3 元/份 每月下载150
全站内容免费自由复制
VIP包月下载
特价:29 元/月 原价:99元
低至 0.3 元/份 每月下载150
全站内容免费自由复制
注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信:xuecool-com QQ:370150219