关闭

探讨GTK+应用程序的优化方法

标签: gtk优化signalcachemakefileflash
7717人阅读 评论(4) 收藏 举报
分类:

探讨GTK+应用程序的优化方法

转载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>

随着应用程序功能的完善,时间和空间性能的优化已经成为我们目前工作的重心了。坦白的说,我并不擅长软件优化,甚至可以说我从心里不愿去做优化的工作,因为优化往往伴随破坏软件架构的副作用。但是到了目前这个阶段,整个系统在性能上的表现仍然不尽人意,已经不能再回避了。这里总结一下我所想到的基于GTK+应用程序的优化技巧,算是抛砖引玉吧,欢迎各位高手指教。

1.使用DirectFB作为GTK+的后端

DirectFB支持多进程模式,它用一个称为fusion的内核模块作为通信中介,用Master/Slave模型取代像TinyX所用的C/S模型,进行窗口操作时,不需要进程间切换,这在一定程度上改善了应用程序的响应速度。

2.使用DirectFB的pixbuf优化

DirectFB提供了一个pixbuf的主题引擎的优化实现,其实效果不错,我们在使用pixbuf的主题引擎时,感觉性能几乎没有下降。

3.延迟加载

强大的个人信息管理(PIM)是我们平台的特点之一,像名片,彩信,邮件,短信和日程等数据,我们没有加入硬性的限制,只受限于flash空间的大小。那么在显示列表时,一次性加载太多数据,必定会导致响应速度的急剧下降。为此我们对数据库和treeview做了一些改进,使用延迟加载的方式,只加载当前显示的和即将要显示数据,这不但节省了内存空间,也提高了响应速度。重要的是,加载速度随数据库容量的增加,只有微小的变化。

4.使用新的model代替model.clear

treeview和combobox等控件都使用了model,而model的特性是变化会通知view更新界面。这本来是它的优点,但有时在性能上却成为致命的因素,在重新刷新数据时,如果调用model的clear函数,该函数每删除一条数据都会触发一次signal让view更新,这使得重新加载数据变得非常非常慢。后来我们改为创建一个新的model,加载数据后再设置到view中,这样界面就只用更新一次。

同理,在第一次加载数据时,也要先把model的数据初始化好后,再设置到view里,而不是一条一条的插入进去,那样每插入一条都要更新一次界面,也会造成严重的性能影响。

5.Block signal

与前面一条类似,有的操作会触发signal,比如插入数据,会引发其它对象的更新。通常只是做一次操作,这没有什么问题,但在批量操作时,所造成的影响就变得显著了。这时可以先block住signal,等批量操作完成后,一次性触发signal。

6.在idle中加载数据

通常的做法是在应用程序初始化时,加载所有相关数据,这种方法让程序设计变得简单,但常常让应用程序起动变得缓慢。起动变得慢了,可能会让用户误以为没有点中起动图标,而多次重复去点击起动图标,造成重复起动应用程序,虽然我们可以保证应用程序单实例运行,但仍然会有一些系统开销,更重要的是起动缓慢会让用户不满意。这时可以先显示界面,让数据加载在idle里完成,等用户真正去操作界面时,数据也加载得差不多了。

7.使用动画标识当前的状态

这个方法并不能提高程序的速度,相反还有一些系统开销,不过它可以让用户知道当前状态,而不至于在界面没有反应时抓狂。这可以在应用程序起动过程中,显示一个动画标识正在起动,我们支持startup-notification功能,所以可以在桌面里统一处理,而应用程序自身不需要关心。

8.Prelink

prelink的功能正如其名字所示,事先link好所有共享库,避免在加载时才link带来的开销。它不仅可以加快起动速度,还可以减少内存开销。不过它虽然有点效果,但不要对它指望太高。

9.去掉DirectFB中不必要的驱动程序。

以前发现DirectFB中有很多共享库,它们是用于显卡加速的,里面有空间不小的data段,由于这些data段是私有的,而且它们在很多进程中加载,所以占去不少空间。我们并不需要这些加速驱动程序,可以直接删除它们。

10.只读数据用const修饰

我们知道const数据是放在rodata段中的,它在所有进程之间共享,即只加载一份拷贝。而普通数据是放在data段里的,它是进程私有的,每个进程都要加载一份拷贝。所以对于只读的数据,一定要加const修饰符,让数据放在rodata段中,可以节省不少内存空间。

11.尽量减少进程数

进程数的增加对于内存有很大影响,这不仅是因为内核创建进程时所占的空间,更重要的是,一般的进程都会链接很多共享库,每个共享库的中data/bss都要分配私有空间,最终占去很大的RAM空间。所以减少进程数对RAM空间的节省有明显的效果,合并进程,同时减少了进程间的通信开销,这也有利于提高时间性能。我们把SCIM从7个进程合并为两个进程后,输入法的响应速度有明显提高,占用的RAM空间也减少了。我们现在着手进一步合并后台服务进程。

12.使用Thumb指令代码ARM指令

Thumb是16位的,把程序编译成thumb指令,可以大大降低代码段的大小,不但节省了flash空间也节省了RAM空间。同时由于数据总线是16位的,程序的运行速度较ARM指令也有很大提高。我们目前正在研究,从可行性上讲,应该没有什么问题。

13.减少不必要的依赖。

前面说了共享库的data/bss段是私有的,每个进程都有一份拷贝。而gcc并没有在链接时做优化处理,不管你是否真正调用了某了个库的函数,只要在命令行参数指定该库,它就会链接进来。所以在创建Makefile时,一定要尽量避免链接不必要的共享库。

附带说明一下,linux的内核虽然实现了父子进程之间的Copy on write技术,避免不必要的内存复制,但这对data/bss段并没有什么意义,原因是fork之后,在执行exec之类的函数时,整个进程空间已经被替换了。data/bss并不会在父子之间共享,相反,它们立即就被创建并初始化。

14.Minimo用gtk编写而不用XUL编写

我们用gtk重写minimo之后,minimo起动速度由原来的40秒,提高到了后来的7秒,看来XUL的性能表现确实不佳。后来又采用idle加载机制,现在从起动到显示界面仅要2到3秒钟的时间。

15.dlopen 代替exec

我记得ALP使用了这一招,据说效果不错。它把应用程序编译成共享库而不是可执行文件,在执行时先fork一个进程,再通过dlopen把应用程序加载进来运行。这样仍然是多进程的,但是避免了exec这样的费时的操作,同时可以享受父子进程之间的copy on write技术带来的性能提升。不过令人我惊异的是,我的实验结果得到却是相反的数据,有时间我再分析一下。

16.cache压缩

有一种称为cache压缩的技术,它可以让cache压缩后存放在内存中,把省出的空间供其它进程使用。我们还没有来得及测试,有兴趣的朋友可以参考http://linuxcompressed.sourceforge.net

17.Gtkrc cache

据说Nokia的N770/N800为加快应用程序的起动速度,使用了一种称为gtkrc cache的技术,具体细节不太清楚,大概可能是把gtkrc从原始的文本文件存为二进制文件,使得gtkrc的加载速度变快吧。

18.XIP

XIP可以让应用程序直接在norflash中执行,而不必加载的内存中执行。我们没有使用norflash,所以也就没有去研究,不过这种思想倒是挺不错的。

19.使用压缩的文件系统

像ramfs,jffs 2和yaffs 2等文件系统都可以压缩,压缩比率也相当可观,能够节省大量的flash空间。我们目前使用的jffs 2,据说yaffs 2表现更为优异,等测试之后,再决定最终使用哪一种吧。

20.挖掘硬件加速功能

现在的CPU一般都带有硬件加速功能,特别是多媒体相关的应用,比如硬件编解码和MMX指令等等,这对多媒体应用程序相当有效,要充分发挥,不用白不用,不要浪费了。

21.使用更小的函数库

现在不少函数库,都有多种实现,可以考虑使用针对嵌入式设备优化过的版本。比如uclibc代替glibc等等,虽然介于以前使用uclibc的经验,我决不赞成使用uclibc,但类似的方法还是可取的。

22.奉行简约主义

像词典的词库,输入法的词库和字体等数据,都要尽量精简到最少,不能像PC那样有多少就放多少。

在以上方法中,有的可以同时提高时间和空间性能,有的只能提高时间性能或者空间性能,还有的是时间性能和空间性能互换的,要根据具体情况进行取舍。

希望各位高手不吝赐教。

~~end~~
 
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:3901037次
    • 积分:46909
    • 等级:
    • 排名:第69名
    • 原创:664篇
    • 转载:66篇
    • 译文:1篇
    • 评论:3099条
    最新评论