在C++中优化空间而不是速度

Emi*_*ier 42 c++ embedded optimization

当你说"优化"时,人们倾向于认为"速度".但是速度并不是那么关键的嵌入式系统呢,但内存是一个主要的限制因素呢?什么是一些指南,技术和技巧可用于削减ROM和RAM中的额外千字节?一个"配置文件"代码如何查看内存膨胀的位置?

PS One可能会争辩说,在嵌入式系统中"过早地"优化空间并不是那么邪恶,因为你给自己留下了更多的数据存储空间和功能蠕变.它还允许您降低硬件生产成本,因为您的代码可以在较小的ROM/RAM上运行.

PPS也欢迎参考文章和书籍!

PPPS这些问题密切相关:404615,1561629

Tim*_*ith 30

我从极其受限的嵌入式内存环境中获得的经验:

  • 使用固定大小的缓冲区 不要使用指针或动态分配,因为它们有太多的开销.
  • 使用有效的最小int数据类型.
  • 不要使用递归.始终使用循环.
  • 不要传递大量的函数参数.请改用全局变量.:)

  • 全局或许多函数参数的一种替代方法是使用参数块.基本上,您创建一个可由多个函数使用的`struct`,每个函数都使用PB中需要的任何参数.然后,调用代码可以设置PB并将其传递给一个或多个函数.旧Mac OS中的低级文件系统调用从一开始就实现了这一点,以帮助将所有内容打包到原始Macintosh的128K中.它就像贫民窟的类,除了(与类方法不同),你可以将两个PB传递给某些函数. (8认同)
  • 你会被最后一个火炬点燃,哈哈.:) (7认同)
  • 实际上,如果你考虑一下人们如何习惯在内存受限的系统上编程(以及随后的两位数年份问题,但这是一个不同的故事),这是完全合理的.这种类型的程序架构将小得多.你会真的很惊讶人们设法适应真正的小型计算机系统(在真正的程序员的时代;-). (3认同)
  • 我假设每个人都在谈论经验......他们还有什么资格?!:d (2认同)

Jam*_*mes 13

你可以做很多事情来减少你的记忆足迹,我相信人们已经有关于这个主题的书籍,但是一些主要的是:

  • 编译器选项以减少代码大小(包括-Os和打包/对齐选项)

  • 用于去除死代码的链接器选项

  • 如果您从闪存(或ROM)加载到ram执行(而不是从闪存执行),则使用压缩的闪存映像,并使用引导加载程序对其进行解压缩.

  • 使用静态分配:堆是一种分配有限内存的低效方法,如果它受到约束,则可能由于碎片而失败.

  • 查找堆栈高水印的工具(通常它们用模式填充堆栈,执行程序,然后查看模式保留的位置),这样您就可以最佳地设置堆栈大小

  • 当然,优化用于内存占用的算法(通常以牺牲速度为代价)

  • @Emile:在非常有限的环境中,由于严格的限制,你经常需要打破"好的"编程实践. (5认同)
  • 那,并且不必在任何地方处理故障,您可以节省大约30%的代码大小;-) (2认同)

Emi*_*ier 12

一些明显的

  • 如果速度不重要,请直接从闪存执行代码.
  • 使用声明常量数据表const.这样可以避免数据从闪存复制到RAM
  • 使用最小的数据类型紧密打包大型数据表,并以正确的顺序避免填充.
  • 对大型数据集使用压缩(只要压缩代码不超过数据)
  • 关闭异常处理和RTTI.
  • 有没有人提到使用-Os?;-)

将知识折叠成数据

Unix哲学的规则之一可以帮助使代码更紧凑:

表示规则:将知识折叠成数据,因此程序逻辑可以是愚蠢和健壮的.

我无法计算有多少次我看到精心设计的分支逻辑,跨越许多页面,可以折叠成一个很好的紧凑的规则,常量和函数指针表.状态机通常可以这种方式表示(状态模式).命令模式也适用.这完全是关于声明性和命令式编程风格.

记录代码+二进制数据而不是文本

而不是记录纯文本,记录事件代码和二进制数据.然后使用"短语手册"重新构建事件消息.短语中的消息甚至可以包含printf样式的格式说明符,以便事件数据值在文本中整齐地显示.

最小化线程数

每个线程都需要它自己的内存块用于堆栈和TSS.如果您不需要抢占,请考虑让您的任务在同一个线程内协同执行(协作多任务).

使用内存池而不是囤积

为了避免堆碎片,我经常看到单独的模块囤积大型静态内存缓冲区供自己使用,即使只是偶尔需要内存.可以使用内存池,因此内存仅"按需"使用.但是,这种方法可能需要仔细分析和检测,以确保池在运行时不会耗尽.

仅在初始化时动态分配

在只有一个应用程序无限期运行的嵌入式系统中,您可以以一种不会导致碎片的合理方式使用动态分配:只需在各种初始化例程中动态分配一次,并且永远不会释放内存.reserve()你的容器到正确的容量,不要让它们自动生长.如果需要频繁分配/释放数据缓冲区(例如,对于通信数据包),则使用内存池.我甚至扩展了C/C++运行时,以便在初始化序列之后尝试动态分配内存时,它会中止我的程序.


Tho*_*ews 7

从链接器生成映射文件.它将显示如何分配内存.在优化内存使用时,这是一个很好的开始.它还将显示所有功能以及代码空间的布局方式.


Chi*_*Uni 7

与所有优化一样,首先优化算法,然后优化代码和数据,最后优化编译器.

我不知道你的程序是做什么的,所以我不能对算法提出建议.许多人都写过关于编译器的文章.所以,这里有一些关于代码和数据的建议:

  • 消除代码中的冗余.任何重复的代码都是三行或更多行,在代码中重复三次,应该更改为函数调用.
  • 消除数据中的冗余.找到最紧凑的表示:合并只读数据,并考虑使用压缩代码.
  • 通过常规分析器运行代码; 消除所有未使用的代码.


Nik*_*sov 5

这是一本关于这个主题的书.小内存软件:内存有限的系统模式.