【解析新特性】300W单路输出工业电源>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » [求助]再问tornado中代码的执行效率问题

共9条 1/1 1 跳转至

[求助]再问tornado中代码的执行效率问题

菜鸟
2007-04-08 20:09:00    评分
1.tornado的编译规则可否修改?
因为目前我的代码大概有7,8千行,但是编译出来有30M多,是不是通过修改编译规则可以将其缩小?
另外,有没有优化的编译规则使代码效率比默认的规则编译出来的运行速度快?
2.标准c语言中有寄存器变量,那么在tornado中是否可以声明寄存器变量?如果可以声明,是不是使用时
要有些设置或者写代码时有特别的规则?用了寄存器变量之后速度肯定能提高不?

多谢各位指点!!



关键词: 求助     再问     tornado     代码     执行     效率     问题    

菜鸟
2007-04-09 05:02:00    评分
2楼

我的见解,仅供参考

1. 7000~8000行代码编译出来30M,实为少见。我认为有两个可能的原因:

其一:在编译过程中添加了调试信息,也就是编译器的选项中使用了 -g(如果是gcc,或者dcc)选项,这时,目标文件中添加了调试信息,优化级别应该是0,这往往会造成目标文件大小数十倍的增加。如果去掉调试信息,启用优化(一般VxWorks的应用程序用gcc编译选项用-O2),目标文件应该会小很多。Tornado的“build”TAB中有此选项,而且这种原因的可能性较大些。

其二:程序代码的数据段占用非常大,也就是说程序中存在大量的初始化了的全局变量或者静态变量。如果全局变量和静态变量可以不初始化,可以尝试不对其进行初始化(不设置初始值),这样,他们将存在bss段中,目标代码中仅使用占位符进行定义。这个可能性不太大,影响也不十分明显(视你的程序而定)。

2. VxWorks中允许使用寄存器变量,通常为了方便和各平台兼容,使用FAST作为寄存器变量的前置修饰符,例如:FAST int myVar;寄存器变量使用是有一定限制的,也是有其目的的,从物理上来说,使用寄存器变量的好处就是避免与外部存储总线打交道,缩短该变量的访问时间。因为寄存器是有限的,所以通常寄存器变量不能过多使用,一般用于非常频繁访问的函数的非常频繁访问的局部变量。但这种寄存器变量在有缓存且启用了缓存的系统上,效果不是十分明显,对于程序效率的影响不会特别大。非常推荐你看看WindRiver自己写的那些源代码,里面可以找到很好的参考。

仅供参考,且盼反馈。


菜鸟
2007-04-09 17:54:00    评分
3楼
不太可能吧,8000行代码即使没行160个字符,都变成二进制,也只有1M多啊,怎么可能有30M呢,是不是elf啊?

菜鸟
2007-04-09 22:39:00    评分
4楼
yaopg兄:
首先多谢你的回复,受益非浅。
但是我没有在tornado的build菜单中找到gcc或者dcc的选项,是不是tornado版本的问题?我的是tornado2.2,盗版的。是不是正版的才有这两个规则?我的到是有个gnu选项,不知道是不是编译规则?
另外我的程序代码中确实定义了几个比较大的缓冲区,大概总共是二十多兆的空间(原来是30几兆),也给它们赋了初值,编译出来的结果是这样的:vxWorks: 661568(t) + 21026224(d) + 33552(b) = 21721344
如果不赋初值出来的结果是这样的:vxWorks:661568(t) + 460464(d) + 20599376(b) = 21721408
不知道这里面的t、d、b分别代表的是什么?
寄存器变量的问题我原来用的是register,速度未见提升,回头我用FAST再测一下试试。
再次感谢!

菜鸟
2007-04-09 22:41:00    评分
5楼
woodhead兄:
我的程序代码中定义了几个比较大的缓冲区,大概总共是二十多兆的空间(原来是30几兆),也给它们赋了初值,编译出来的结果是这样的:vxWorks: 661568(t) + 21026224(d) + 33552(b) = 21721344
如果不赋初值出来的结果是这样的:vxWorks:661568(t) + 460464(d) + 20599376(b) = 21721408

是不是可以优化呢?

菜鸟
2007-04-10 00:11:00    评分
6楼

to taocj:

关于gnu和diab,在Tornado2.2的发行版中,有些是没有带diab C 编译器的(比如x86、ARM等),有些是有diab编译器的(比如ppc,CF等)。用什么编译器,通常对我们进行编程的影响并不大,就Vxworks的编译器而言,diab可能确实是好些,主要表现在编译出来的目标文件大小大概为gnu的95%左右。毕竟是WindRiver自己的产品,而且是收费的。但用gnu编译器也是非常不错的,有没有diab编译器,与D版与否无关。

关于t、d、b。t是text的意思,亦即纯代码,也就是常说的文本段;d是data的意思,亦即数据,也就是常说的数据段;b是bss的意思,就是没有初始化的全局变量和静态变量,也就是常说的bss段。

对于你的情况:661568(t) + 21026224(d) + 33552(b) = 21721344

文本(代码):约为660k,不算大,但也不小了。

数据(初始化了的全局变量和静态变量):约为21M,超大。

bss(没初始化的全局变量和静态变量):约为33k,不大不小。

从上面可以看出,数据段超大是造成你目标文件大的主要原因。

有两种改进意见仅供参考:

第一: 仅定义全局变量,但不初始化它,或者如果必须要初始化的话,在代码中初始化它,也就是说,在程序中,如果有下面现象:

int myArray[1000000] = {1,2};

则将其改为:

int myArray[1000000];

int myFunction(...)

{

/* perform init */

myArray[1] = 1;

myArray[2] = 2;

...

}

第二:如果你的缓冲是那种“一分配则在本次上电运行过程中不再释放,不再换地方存储”(其实,你声明全局变量就是这样的。),还有一种比较好的方法可以实现,那就是将你的静态内存分配改为动态内存分配。简单的做法就是使用malloc()函数。例如:

void * myBuf_Ptr = NULL;

STATUS myFunction(...)

{

char * pBuf;

myBuf_Ptr = malloc(100000000);

if (myBuf_Ptr == NULL)

{

/* 内存分配失败,报错 */

logMsg("memory allocate Error.%s,%s,%d",(int)__FILE__,(int)__FUNCTION__,(int)__LINE__,0,0,0);

return ERROR;

}

/* 得到了一个动态的空间。可以使用它作为缓冲区了。 */

pBuf = myBuf_Ptr;

/* 必要的初始化 */

pBuf[0] = 1;

pBuf[1] = 2;

...

}

如果你的缓冲区是结构体的话,做法也类似。

需要注意的是,malloc()的使用需要十分小心,通常情况下,如果分配了“一直”不要释放的问题不大,但如果频繁分配又释放,在嵌入式操作系统中是不推荐的,特别是VxWorks 5.x。它没有“堆”的管理,分配释放做多了,容易有内存碎片,日久容易造成不稳定。

另外就是,使用动态分配,特别注意对动态内存空间指针的合法性进行判断,一定要避免使用空指针。

其实,C语言作为一种“微内核”(不需要啥基本库支持就能运行,其实如果不需要标准库支持,它只需要堆栈指针就可以了)的编程语言,灵活性是很强的,对于我们做嵌入式来说,研究它的行为是十分有趣的,比如说:C语言的每一句话究竟会产生怎样的汇编代码和CPU动作呢?参数传递是如何进行的?等等。

关于寄存器变量,我觉得:使用与否可按前面我提到的“频繁”与否的规则来进行,但要看出它的效果(特别是宏观上变快了与否),是不那么明显的,通常在驱动程序、中断服务程序和频繁调用的系统基本函数比如系统IO函数中,才会使用到寄存器变量,而在应用程序中,用到的并不多。

还有,那个FAST通常就是register。WindRiver为了代码一致性好,就定义这个宏。

再盼反馈。


[align=right][color=#000066][此贴子已经被作者于2007-4-9 16:24:24编辑过][/color][/align]

菜鸟
2007-04-10 03:49:00    评分
7楼

再咯嗦几句:

代码效率其实主要依赖于程序的实现,算法等。还有一些辅助手段:

寄存器变量:对于“整类型”的局部变量,使用寄存器变量可以减少通过RAM总线访问(普通局部变量)的时间);

内联函数:对于调用频率非常高的小尺寸代码,使用内联函数(inline),通过牺牲代码尺寸来减少堆栈操作。

另外,你可以写个 test case,如下:

case1:

int myArray[100000000] = {1};

void myFunc(void)

{

myArray[0] = 1;

return;

}

case 2:

int myArray[100000000];

void myFunc(void)

{

myArray[0] = 1;

return;

}

分别编译,比较一下目标文件尺寸,然后,对于case2,启用和关闭调试信息,再比较目标文件尺寸。 最后,比较一下二者所生成的汇编代码分别是怎么样的。就可以很清楚的发现初始化与否的区别,data和bss的区别,占位符的含义。


菜鸟
2007-04-12 04:48:00    评分
8楼

1.我想先不使用分配动态内存的方法。

将我的全局变量不初始化后编译的情况是:vxWorks:661568(t) + 460464(d) + 20599376(b) = 21721408

也就是说b大了,d小了,但是总体的大小还没有变,怎样将总体的大小变小呢?

2.关于寄存器变量,我的算法中要频繁对一个变量进行赋值操作,对变量的赋值应当是对外部存储器操作吧。因此考虑用它来提高代码的效率。但是使用之后效果确实不是很明显,有的资料上说嵌入式系统中可能会把寄存器占满,从而用户无法用到寄存器变量,不知道有没有这方面的原因?

3.内联函数的问题,我也考虑过,但是在函数前加上inline后编辑后会报错(syntax error),不知道是什么地方设置的不对,还是tornado不支持?

4.你最后提到的看二者之间所编译的汇编代码的区别不知道从何处看?另外启用和关闭调试信息在什么地方设置呢?

感谢在不言中……


菜鸟
2007-04-12 05:50:00    评分
9楼

1. vxWorks映象,为了避免内存检测错误等目的(具体不详),是把bss象data那样处理了的,它的目的是将bss段清零。这是它的编译规则,我们通常最好不要修改它(如果要修改也是可以的,可以改target/h/tools中的链接脚本和target/h/make中的编译规则)。如果要改变总体大小,有两种方法:
其一:把应用程序和VxWorks系统编译成一个文件,使用ROM类映象,vxWorks_romCompress(,hex,bin)或者不压缩的(当然还是压缩好,需要的ROM空间小呀)。这类映象是把bss清零的过程省略掉了的,bss的大小影响就消除了。
其二:把VxWorks系统映象和应用程序编译成不同的文件。待VxWorks启动成功后,加载应用程序文件。条件是你的系统必须要有某种文件系统支持,比如Flash的tffs或者CF的dosFS卡等。这样,系统映象与应用程序独立,应用程序编译成一个.out文件,尺寸是非常小的(前面你做的测试已可看出)。
当然,如果这两种你都不愿意采用,那可能malloc()又成了最好的选择。

2. 有这种可能,这不仅与系统的寄存器使用有关,还与你的编译规则有关。对于你的情况,VxWorks映象属于操作系统,其中的大部分代码与硬件相关,都是不能优化过头的了,可以观察下,sysLib.c等文件使用了-fvolatile来强制其访问的存储器是不可缓存的,优化级别也很低,不同优化级别的代码执行起来的效果是有所差别的(生成的汇编也截然不同),所以,还是建议你单独编译应用程序,使用较高的优化级别(-O2通常)。关于寄存器的使用情况你可以在target/h/arch/<yourCPU>/asmxxx.h中找到,文档可以在编译器参考手册中找到。实际上,前面已提到,如果你的CPU有Cache的话,使用寄存器变量带来的好处是微乎其微的,因为cache的速度也非常快。再说,就算没有cache,使用resigter与否,宏观效果是很难区分的。

3. 内联函数可用。可以在Vxworks自己的那些源码和头文件系统找到范本。搜索一下就可以得知了。

4. 看汇编,借用一些反汇编工具。个人推荐IDA Pro,支持的CPU架构比较多,使用方便,网上又好找。说实话,一个反汇编工具可以是搞嵌入式的较为必须的工具呀。

5. 启用和关闭调试信息:tornado的工程中的build栏里面是有的,在C/C++ complier那个里面。加调试信息是 -O -g,不加是 -O2 (-g就没有了)。。其实,推荐自己写makefile在命令中编译,方便呀,tornado的代码编辑能力又实在不敢恭维。

不必言谢。共同学习。你的钻研精神好强呀。。必有作为!



共9条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]