【问题标题】:How can I optimize GCC compilation for memory usage?如何针对内存使用优化 GCC 编译?
【发布时间】:2015-04-24 17:35:40
【问题描述】:

我正在开发一个应该使用尽可能少内存的库(我不关心其他任何事情,比如二进制大小或速度优化)。

我可以使用任何 GCC 标志(或任何其他与 GCC 相关的选项)吗?我应该避免某种程度的 -O* 优化吗?

【问题讨论】:

  • 我实际上不知道控制虚拟内存使用的 any 编译器的任何编译器选项。这几乎完全由您的代码控制。编译器唯一控制的是二进制大小。
  • 编译器只能尝试减小代码大小(使用-Os)。您使用的内存量完全取决于您——当您请求 malloc() 时,您是否希望它拒绝?
  • 如果您的架构支持未对齐的数据获取,您可以防止结构内的填充。 -fpack-struct 用于 gcc。
  • @holgac 如何检查我的架构是否支持未对齐的数据获取?
  • 您可能可以在 Internet 上找到有关您的 CPU 的信息。如果没有,请查看您的 CPU 规格。例如,x86 和 x86_64 架构支持它,但会导致严重的性能损失。但 SPARC 或 i64 中未对齐的内存访问会导致错误。

标签: c gcc


【解决方案1】:

您的库 - 或惯用 C 中的任何代码 - 有几种内存使用情况:

  • 二进制代码大小,实际上-Os 应该优化它
  • 堆内存,使用C dynamic allocation,即malloc;您显然应该知道如何分配以及分配多少堆内存(以及后来的free-d)。实际内存消耗取决于您特定的 malloc 实现(例如,许多实现,在调用 malloc(25) 时实际上可能消耗 32 个字节),而不是编译器。顺便说一句,您可能会设计您的库以使用一些 memory pools 甚至实现您自己的分配器(在操作系统系统调用之上,如mmap,或在malloc 等之上......)
  • 局部变量,即调用栈上的call frames。这主要取决于您的代码(但优化编译器,例如 -Os-O2 用于 gcc,在优化时可能会使用更多的寄存器,并且可能会使用更少的堆栈)。您可以将-fstack-usage 传递给gcc 以要求它提供每个调用帧的大小,并且您可以给-Wstack-usage=len 以在调用帧超过len 个字节时收到警告。
  • 全局或静态变量。您应该知道它们需要多少内存(并且您可以使用 nm 或其他一些 binutils 程序来查询它们)。顺便说一句,小心将函数内的某些变量声明为 static 会降低堆栈消耗(但您不能对每个变量或每个函数都这样做)。

还要注意,在一些有限的情况下,GCC 正在执行tail calls,然后堆栈使用率降低(因为调用者的堆栈帧在被调用者中重用)。 (另见this old question)。

您还可以要求编译器打包一些特定的struct-s(请注意,这可能会显着降低性能)。你会想要使用一些type attributes,比如__attribute__((packed)),等等......也许还有一些variable attributes等等......

也许您应该阅读更多关于 Garbage Collection 的信息,因为 GC 技术、概念和术语可能是相关的。见this answer

如果在 Linux 上,valgrind 工具也应该很有用...(在调试阶段,最近 GCC 的-fsanitize=address 选项)。

您也许还可以使用一些code generation options,例如-fstack-reuse=-fshort-enums-fpack-struct-fstack-limit-symbol=-fsplit-stack要非常小心:一些这样的选项会使你的二进制代码不兼容与你现有的 C(和其他!)库(然后你可能需要重新编译所有使用的库,包括你的 @ 987654334@,具有相同的代码生成标志)。

您可能应该通过compiling and linking with -flto 启用link-time optimizations(在添加其他优化标志,如-Os)。

您当然应该使用最新版本的GCC。请注意,GCC 5.1 已于几天前(2015 年 4 月)发布。

如果您的库足够大,值得付出努力,您甚至可以考虑使用MELT 定制您的 GCC 编译器(以帮助您了解如何减少内存消耗)。这可能需要数周或数月的工作。

【讨论】:

    【解决方案2】:

    使用“栈帧”有很多好处,但确实会使用更多的栈空间来保存栈帧指针。

    您可以告诉编译器不要使用堆栈帧。这(通常)会略微增加代码大小,但会减少使用的堆栈数量。

    您只能使用 char 和 short 作为值而不是 int。

    这是一种糟糕的编程习惯,但可以为多种目的重复使用变量和数组。

    如果某些变量集在使用上是互斥的,那么可以将它们放在一个联合中。

    如果函数参数列表都很​​短,那么编译器可以将所有参数传递到寄存器中。 (拥有大量通用寄存器的架构在这里真的很有帮助。

    只使用一个包含所有 malloc 类型操作所需区域的 malloc,以尽量减少分配的内存开销。

    有很多技巧。大多数使代码更难调试/维护,并且通常使代码更难人类阅读

    【讨论】:

      【解决方案3】:

      如果可能,您可以使用 -m32 选项将您的应用程序编译为 32 位。因此,应用程序将只消耗 64 位系统上一半的内存。

      apt-get install libc6-dev-i386
      gcc -m32 application.c -o application
      

      【讨论】:

        猜你喜欢
        • 2016-12-09
        • 2011-08-11
        • 2020-04-14
        • 2015-08-31
        • 2020-09-07
        • 2013-10-07
        • 2012-03-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多