入式实时程序设计中C/C++代码的优化

来源:互联网 发布:纽约时报 知乎 编辑:程序博客网 时间:2024/05/16 10:09

对於设计嵌入式Linux 系统的研发人员来说,有一个问题是必须要考虑到的,那就是记忆体的空间。

  我们知道嵌入式Linux 系统所用的记忆体不是软碟、硬碟、ZIP 盘、CD-ROM、DVD 这些众所周知的大容量常规记忆体,它使用的是例如Rom, CompactFlash,M-Systems 的 DiskOnChip,SONY 的MemoryStick,IBM 的MicroDrive 等体积极小,与主板上的BIOS 大小相近,存储容量很小的记忆体。所以怎样尽可能的节省空间就显的很重要。

 

  嵌入式系统的记忆体中放置的无非是内核,文件系统,软体,以及自己开发的程式。本文就从程式入手,以一个非常简单的C 程式来作例子,通过三步来让它减肥。

  Hello.c:
  #include <stdio.h>
  int main ()
  {
    printf ("hello,world");
    return 0;
  }

  我们先用正常的编译方法来编译,看看生成的程式的大小是多少
  #gcc -o hello hello.c
  #ls -l hello

  -rwxr-xr-x 1 root root 11542 Nov 13 20:07 hello
  从结果可以看到正常编译後的程式大小是11542Byte

  现在开始我们的三步减肥,看看到底效果如何。
  步骤一:用gcc 的代码优化参数

  代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然後对其重新进行组合,目的是改善程式的执行性能。GCC 提供的代码优化功能非常强大,它通过编译选项-On 来控制优化代码的生成,其中n 是一个代表优化级别的整数。对於不同版本的GCC 来讲,n 的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0 变化到2 或3。

  编译时使用选项-O 可以告诉GCC 同时减小代码的长度和执行时间,其效果等价於-O1。在这一级别上能够进行的优化类型虽然取决於目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。选项-O2 告诉GCC 除了完成所有-O1 级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项-O3 则除了完成所有-O2 级别的优化之外,还包括回圈展开和其他一些与处理器特性相关的优化工作。通常来说,数位越大优化的等级越高,同时也就意味著程式的运行速度越快。许多 Linux 程式师都喜欢使用-O2 选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理想的平衡点。

  #gcc -O2 -o hello hello.c
  #ls -l hello
  -rwxr-xr-x 1 root root 11534 Nov 13 20:09 hello

  优化过的程式的大小是11534Byte,比正常编译的结果11542Byte 似乎没有小多少,不过不用著急,这才是第一步。我们接著往下进行。
  步骤二:用strip 命令
  我们知道二进位的程式中包含了大量的符号资讯(symbol table),有一部分是用来 gdb 除错提供必要帮助的。可以通过readelf -S 查看到这些符号资讯。
  #readelf -S hello
  Section Headers:
  [Nr] Name Type
  [ 0] NULL
  [ 1] .interp PROGBITS
  [ 2] .note.ABI-tag NOTE
  [ 3] .hash HASH
  [ 4] .dynsym DYNSYM
  [ 5] .dynstr STRTAB
  [ 6] .gnu.version VERSYM
  [ 7] .gnu.version_r VERNEED
  [ 8] .rel.dyn REL
  [ 9] .rel.plt REL
  [10] .init PROGBITS
  [11] .plt PROGBITS
  [12] .text PROGBITS
  [13] .fini PROGBITS
  [14] .rodata PROGBITS
  [15] .eh_frame PROGBITS
  [16] .data PROGBITS
  [17] .dynamic DYNAMIC
  [18] .ctors PROGBITS
  [19] .dtors PROGBITS
  [20] .jcr PROGBITS
  [21] .got PROGBITS
  [22] .bss NOBITS
  [23] .comment PROGBITS
  [24] .debug_aranges PROGBITS
  [25] .debug_pubnames PROGBITS
  [26] .debug_info PROGBITS
  [27] .debug_abbrev PROGBITS
  [28] .debug_line PROGBITS
  [29] .debug_frame PROGBITS
  [30] .debug_str PROGBITS
  [31] .shstrtab STRTAB
  [32] .symtab SYMTAB
  [33] .strtab STRTAB
  类似於.debug_xxxx 的就是用来gdb 除错的。去掉它们不但不会影响程式的执行还可以减小程式的size。这里我们通过strip 命令拿掉它们。
  #strip hello
  #ls -l hello
  -rwxr-xr-x 1 root root 2776 Nov 13 20:11 hello
  程式立刻变成2776Byte 了,效果不错吧。让我们再接再厉,进行最後一步。
 
  步骤三:用objcopy 命令
  上一步的strip 命令只能拿掉一般symbol table,有些资讯还是没拿掉,而这些资讯对於程式的最终执行是没有什么影响的。如:.comment; .note.ABI-tag; .gnu.version 就是完全可以去掉的。所以说程式还有简化的余地,我们可以使用objcopy 命令把它们抽取掉。
  #objcopy -R .comment -R .note.ABI-tag -R.gnu.version hello hello1
  #ls -l hello1
  -rwxr-xr-x 1 root root 2316 Nov 13 20:23 hello1
  到这一步,程式的减肥就完成了,我们可以看到程式由正常编译的11542Byte 一下子渐少到2316Byte,效果非常明显。

  小结
  程式容量的减小无疑对嵌入式Linux 系统的设计有著重要的意义,它使我们节省了大量空间,使得我们可以利用这部分空间来完善我们的系统,比如加大内核等等,毕竟这才是我们最终的目的。

原创粉丝点击