Embedded 如何优化或减少嵌入式系统软件中的RAM大小?

Embedded 如何优化或减少嵌入式系统软件中的RAM大小?,embedded,Embedded,我在汽车领域从事嵌入式软件项目。在我的一个项目中,应用软件消耗了几乎99%的RAM内存。可用的实际RAM大小为12KB。我们使用TMS470R1B1 Titan F05微控制器。我做了一些优化,比如在软件中查找未使用的消息并删除它们,但仍然不值得减少RAM。你能建议一些通过软件优化来减少RAM的好方法吗 删除未使用的字符串文字不会对RAM使用产生任何影响,因为它们不存储在RAM中,而是存储在ROM中。代码也是如此 您需要做的是减少实际变量,并可能减少堆栈的大小。我会寻找可以调整大小的数组和未使用

我在汽车领域从事嵌入式软件项目。在我的一个项目中,应用软件消耗了几乎99%的RAM内存。可用的实际RAM大小为12KB。我们使用TMS470R1B1 Titan F05微控制器。我做了一些优化,比如在软件中查找未使用的消息并删除它们,但仍然不值得减少RAM。你能建议一些通过软件优化来减少RAM的好方法吗

删除未使用的字符串文字不会对RAM使用产生任何影响,因为它们不存储在RAM中,而是存储在ROM中。代码也是如此

您需要做的是减少实际变量,并可能减少堆栈的大小。我会寻找可以调整大小的数组和未使用的变量。此外,最好避免动态分配,因为这样做会带来风险


除此之外,您还需要确保诸如查找表之类的常量数据存储在ROM中。这通常可以通过
const
关键字来实现。

与速度优化不同,RAM优化可能需要在整个代码中“这里一点,那里一点”。另一方面,可能会有一些“低垂的果实”

数组和查找表 数组和查找表可以是很好的“低挂果实”。如果您可以从链接器获得内存映射,请检查RAM中的大型项目

检查没有正确使用
const
声明的查找表,这会将它们放在RAM而不是ROM中。尤其要注意指针的查找表,它们需要
const
位于
*
的正确一侧,或者可能需要两个
const
声明。例如:

const my_struct_t * param_lookup[] = {...};  // Table is in RAM!
my_struct_t * const param_lookup[] = {...};  // In ROM
const char * const strings[] = {...};    // Two const may be needed; also in ROM
堆积如山 也许您的链接器配置为堆和堆栈保留了大量RAM,比应用程序所需的要大

如果您不使用heap,您可能会消除它

如果您测量了堆栈的使用情况,并且它处于分配之下,那么您可能能够减少分配。对于ARM处理器,对于几种操作模式,可以有几个堆栈,您可能会发现为异常或中断操作模式分配的堆栈比需要的大

其他 如果您已经检查了简单的节省,但仍然需要更多,那么您可能需要检查代码并保存“这里一点,那里一点”。您可以检查以下内容:

全局变量与局部变量 检查是否不必要地使用了
静态
或全局变量,其中可以使用局部变量(在堆栈上)。我见过一些代码在函数中需要一个小的临时数组,它被声明为
static
,显然是因为“它会占用太多的堆栈空间”。如果这种情况在代码中发生的次数足够多,则实际上可以节省总体内存使用量,从而使这些变量再次成为局部变量。它可能需要增加堆栈大小,但会在减少的全局/静态变量上节省更多内存。(作为一个附带好处,这些函数更有可能是可重入的、线程安全的。)

较小的变量 可以更小的变量,例如
int16\u t
short
)或
int8\u t
char
)而不是
int32\u t
int

枚举变量大小
enum
变量大小可能大于所需大小。我不记得ARM编译器通常做什么,但我过去使用的一些编译器默认将
enum
变量设置为2个字节,即使
enum
定义实际上只需要1个字节来存储其范围。检查编译器设置

算法实现
修改你的算法。一些算法有一系列可能的实现,在速度/内存之间进行权衡。例如,AES加密可以使用动态密钥计算,这意味着您不必将整个扩展密钥存储在内存中。这样可以节省内存,但速度较慢。

确保链接器生成一个映射文件-它将显示RAM的使用位置。有时,您可以找到保存在RAM中的字符串文本/常量之类的内容。有时,您会发现其他人在其中放置了一些未使用的数组/变量


如果您有链接器映射文件,那么也很容易攻击首先使用最多RAM的模块。

以下是我在计算单元上使用的技巧:

  • 从显而易见的开始:尽可能将32位字压缩到16位,重新安排结构以消除填充,减少任何数组中的松弛。如果您有超过八个结构的数组,那么使用位字段将它们压缩得更紧是值得的
  • 取消动态内存分配,使用静态池。恒定的内存占用空间更容易优化,而且您将确保没有泄漏
  • 严格定义本地分配的范围,这样它们在堆栈上停留的时间就不会超过必须的时间。有些编译器很难识别何时处理完变量,并且会将其留在堆栈上,直到函数返回。对于外部函数中的大型对象来说,这可能是不好的,因为外部函数调用到树的深处时,这些对象会消耗掉它们不需要的持久内存
  • alloca()
    在函数返回之前不会清理,所以堆栈浪费的时间可能比预期的长
  • 在编译器中启用函数体和常量合并,这样,如果它看到八个不同的常量具有相同的值,它将只在文本段中放置一个常量,并使用链接器将它们别名
  • 优化可执行代码的大小。如果你有一个硬性的实时截止日期,你就知道你的代码需要以多快的速度运行,所以如果你有多余的性能,你可以在速度和大小之间做出权衡,直到你达到那个点。滚动循环,将公共代码拉入函数中,等等。在某些情况下,如果prolog/epilog开销大于函数体,则通过内联一些函数实际上可以获得空间改进
最后一个只是