嵌入式Linux开发调优之二:应用程序

   上一节中我们简单概述了系统与内核层面的调优。其实,在这一层面,大部分开发人员涉及的工作并不多。通常情况下,我们拿到的内核基本是稳定的,基础的系统应用也多是第三方开源成熟的,所以,并不会对内核和系统进行大的修改。相反,产品开发中,多是与业务相关的应用开发,所以这一节我们重点总结下应用的优化方法。当然了,都是程序,运行原理基本一致,很多应用程序的优化方法,对内核和系统应用也是适用的。

   1 通过top命令查看CPU占用高的线程,找到要优化的目标程序。如上节所说,有时候系统运行变慢,不一定是CPU占用变高,也可能是内存占用高导致,所以通过top命令不仅需要注意CPU占用,也要关注内存占用。

   2 如果出现线程对CPU高占用,可以用strace工具看看在哪些系统调用上消耗了时间。除了该工具,还可以使用上节提到的perf工具,不过该工具需要在编译内核时打开相关配置,如果无法重新编译、烧写内核,则使用受到一定限制。特别的,如果出现线程占用某个CPU核100%的情况,则还可以用GDB来attach到线程,分析调用堆栈。进一步的,如果程序是release版本,则堆栈很可能看不到地址对应的符号,如果程序未重新编译,则可以用addr2line工具协助定位地址对应目标程序中的符号。

   3 如果任务属于事务性的,处理工作不需要大量计算,却又占用较高的CPU,那就需要检查任务设计是否合理,是否有空转的可能。对于事务性任务,没有事件处理时,要及时的挂起,避免无谓的CPU占用。正常优化后,事务性任务大部分时间CPU占用应该都是很低的才对。

   4 如果可能,避免事务性逻辑和数据计算型逻辑混合在一起,也就是避免一个任务既处理事务又处理数据计算。举个例子,事务性任务多由于外部事件或者IO来驱动触发,而这类工作,相对CPU的高速特性来讲,大部分时间是让CPU处于等待状态;数据计算型任务多是内存相关计算,CPU等待时间会少很多,所以,如果二者逻辑混在一起,IO等待可能会影响数据计算的及时性。

   通过以上几步“宏观调控”,应用程序大致可正常运行,接下来就需要微调,进一步精准控制,让CPU花最少的指令完成同样的工作。这样既可以让系统整体更流畅,也可以降低整体功耗。做这步工作的切入点要变成数据计算型任务了,根据计算的特点,做针对性的优化。

   5 查表代替计算。这对于图形图像处理类计算,比较常用。比如BMP转RGB。可以将公式的参数整理成表格,在大数据量处理中,会显著提高速度。

   6 整数代替浮点。浮点运算消耗时间一般要大于整数运算,所以如果有大量浮点运算,则可以考虑是否能够替换为整数运算,特别是对于没有浮点运算单元的CPU。同样的,也可以考虑用加减代替乘除,道理是类似的。

   7 循环检查。循环的特点就是重复多次处理,如果能够优化,就可以明显的降低CPU。之前及后面将要所述的方法都可以应用到循环中。比如本人之前遇到过一个音频声道混合的问题,因为实际需要的是short型数据,但是现在很多CPU都是4字节存取内存,所以实际上消耗的是4字节处理时间。基于该认识,通过转换类型,一次处理两个采样,结果该循环的处理时间降低了一半。还有循环处理应该尽可能的利用cache,避免处理的数据跨度过大,导致cache命中率不够高。再有,就是循环处理内容要尽可能合理,不要太简单,这样造成跳转指令的代价大于实际处理内容。最后,因为是重复处理,所以要尽可能避免重复计算。对于可以固定的值,就不要在循环中每次重复计算了。

   8 位移代替乘除。这个就不需要细说了。

   9 指针代替多参数。这与参数的传递规则有关。特别是对于结构体类型的参数传递,因为是拷贝方式,所以并不是很好,如果用指针,只需要传一个地址值,代价会低不少。

   10 尽量用顺序代码,避免过多分支或长跳转。这其实是充分发挥cache的作用,使代码尽量符合局部性原理,从而提高命中率。

   11 如果可能的话,可以充分利用硬件特性,这在嵌入式开发中是很有可能的。比如驱动中数据的传递,使用DMA代替CPU。本人之前在ST网卡驱动编写中就使用其FDMA替换纯内存的拷贝。应用层,一些图形图像处理中,SOC类芯片可能会提供基于物理内存地址的块拷贝接口,通过虚拟地址转物理地址,就可以利用这类接口,实现快速内存拷贝。

   12 上面是使用硬件辅助拷贝内存,当然,如果可能的话,也可以通过合理的程序设计,比如使用指针传递方式,减少或避免软件层的内存拷贝。

   13 内存这块,还可以充分利用内存池方法。这可以减少内存的分配释放,如果利用合理,也可能减少内存的拷贝,也是一种常用的优化方法。

   14 时间换空间和空间换时间。有时候时间重要,但有些时候,可能空间更重要。可根据具体需求,通过时间换空间(更多的计算减少代码和内存占用)或空间换时间(增加代码和内存使用减少时间消耗)来优化。

   15 最后,现代编译器也越来越强大,越来越智能,可以利用编译器辅助优化。不过高级别的优化选项可能会引发非常隐蔽的逻辑问题,使用时需要考虑到这一点。

猜你喜欢

转载自blog.csdn.net/wwwyue1985/article/details/113096180