早前的专栏中曾讨论过在许多情况下需要优化的嵌入式系统的关键特征,包括系统时序、代码大小、 RAM 使用率和能耗。虽然优化每个特征通常要求不同的方法和技术,但开发人员在优化嵌入式软件时可以遵循几个通用技巧。
技巧1—总是创建基准用于比较
创建基准用于比较优化结果的必要性显而易见,令人惊讶的是开发团队常常在没有任何基准的情况下匆忙开展优化。基准测量很重要,因为每次优化得到的改进会越来越小。举例来说,第一遍能耗优化可能有20%的改进,第二次有10%,第三次5%,以此类推。开发人员应了解这种趋势,并将他们在系统中获得的改进量化为输入次数的函数。
技巧2—设定优化目标
每一次优化都比前一次需要更多的时间才能从系统中获得极少量的改进。开发团队需要仔细平衡他们的时间投入,并根据改进结果判断是否值得花这么多时间。一味闷头做事很容易沉迷,可能花了数周时间才认识到自己在优化一个不再需要优化的系统。因此在优化开始之前,开发团队应设定一个目标值,达到这个目标,就表示优化结果对当前应用来说足够好,优化过程已经完成。
技巧3—使用正确的测量工具
如果没有合适的测量工具,优化一个系统是很困难的。举例来说,如果不使用一种精确的方法来测量系统和微控制器的能耗,便很难完成能耗的优化。开发人员经常无法区分这两种不同的能量测量,他们试图减少实际上无法再减少的微控制器能耗。
对性能优化感兴趣的开发人员可以看一看我在“亲自动手:Segger系统查看工具”中介绍的Segger系统查看工具,这款工具对于了解哪些
函数正在独占 CPU 非常有用。如果没有能够精确测量或可供开发人员查看系统行为的工具,那么在优化系统时便抓不住重点。
技巧4—使用优化工具
为了减小代码大小或提高性能,嵌入式软件的许多方面都可以优化。一些情况下可以使用独立的或附属的工具链。Somnium DRT优化器就是一种很好的优化工具,可以与GCC一起用来优化代码大小、能量使用率和性能。
不过有时候外部工具可能不是必需的,只要选择正确的工具链就足够了。我最近写了一篇题为《开源与商用编译器》的文章,说明了这样一个事实:在Coremark测试中,对于相同的微控制器和相同的测试条件,商用编译器的得分总是高于GCC等开源编译器。
技巧5—使用编译器属性和#pragma指令
我一般很不喜欢用#pragma指令或编译器属性。属性和#pragma指令通常是不可移植的,改变编译器可能会造成软件缺陷。然而,在调整嵌入式软件时,开发人员通常没有选择。使用属性和#pragma指令可以提高速度,并能根据实际情况有选择地优化某个功能。基于这些理由,想要优化软件的开发人员应该熟悉属性的使用,而且要阅读《用C语言编写可移植的优化程序》,这样他们才知道如何编写出可移植的最优程序,并且没有负面影响。
技巧6—多做实验
在优化系统方面没有一成不变的方法,开发人员不应该局限于任何一种特殊的技术。有时候学习和优化系统的最好方法是尝试各种实验并分析其结果。
当我首次为了低功耗而优化系统时,做了很多实验,也出现了一些错误。通过实验过程和所记录的结果,我就能够理解什么有用,什么没用,以及做哪些事是在浪费资源和时间。如何最好地利用printf就是一个简单的例子: 通过尝试不同的驱动模型可以发现,很多方法都可以显著提高开发人员使用printf时获得的实时性能,而人们设想的结果通常远好于真实结果。
技巧7—深入研究编译器产生的指令
在资源特别有限的应用中,开发人员有时只需挽起袖子深入理解编译器产生的指令。在将要执行的三四个广义指令间选择三元操作符而不是if/else是有区别的,这很可能会导致应用程序崩溃。
虽然像C这样的语言是标准的,但每种编译器在优化和产生机器指令时有少许差异。唯一现实的方法是检查汇编语言,了解编译器在做什么。
总结
不同应用程序的优化需求各不相同。小批量产生的应用程序也许根本不需要优化;而对于另外一些应用程序,每个 时钟 周期或每毫微安 电流 都很重要,则可能需要开发人员花大量时间从系统中榨出最后一点性能或能量。虽然每种系统都是不同的,但开发人员若熟记这些技巧,便为实现更高效的系统迈出了可喜的第一步。