在嵌入式系统中为单个阵列分配大量堆栈有缺点吗?


12

我通常可以毫无疑问地确定某些数据是全局的,静态的还是在堆栈上(这里没有动态分配,因此不使用堆)。我也看过几个Q / A如这一个,但我的问题是更具体的,因为它涉及到比较系统内存中的数据,庞大的数额巨大。

我正在尝试改进的现有代码(设计,可能出现的问题,性能等)。该代码在只有4KB RAM的旧8位MCU上运行。在这段代码中,我将使用几乎1KB的数组(是的,在4KB RAM系统上为1KB)。使用此数组的每个字节,这不是问题。问题在于此数组是文件在其中声明的静态数组,因此其生命周期与程序之一相同(即可以视为无限)。

但是,在阅读了代码之后,我才发现该数组不需要无限的生命周期,它是以完全过程性的方式构建和处理的,因此我们应该只能在使用它的函数中声明它,这样,它将在堆栈上,因此我们将节省此1KB RAM。

现在的问题是:这是个好主意吗?从设计的角度来看,如果不需要无限/全局生命周期,则它属于堆栈。但是,嘿,这是4KB中的1KB,这样分配25%的RAM是否没有任何缺点?(可能是堆栈的50%或更多)

有人可以在这种情况下分享经验吗,还是有人在考虑是否有任何合理的理由不将此数组放在堆栈中?我正在寻找技术缺陷以及对设计的评论。

我唯一意识到的是,进入此功能时,我必须确保实际上有1KB的可用堆栈。也许这就是我必须采取的所有措施,也许不是。


4
您写道:“因此节省了1KB RAM”。保存它做什么用?当阵列需要1KB时,该1KB必须可用,为什么不进行静态分配呢?当阵列不需要它时,您还有其他用途吗?
kkrambo

@kkrambo在某些时候,当我们无法在RAM上添加更多内容时,无论它是静态的还是在堆栈上,我们都认为系统已满。如果仅在使用该数组时才将其放在堆栈上,则只要不同时使用它们,就可以放置其他功能。但是问题是完全合法的,目前,如果我们不更改软件中的任何内容,就不需要更多的RAM;)
蒂姆

1
您能否弄清楚此数组是否具有始终保持相同的内容,或者在调用使用它的函数时它是否发生了变化?
Blrfl

@Blrfl每次调用该函数时都会更改。
2016年

Answers:


8

我唯一意识到的是,进入此功能时,我必须确保实际上有1KB的可用堆栈。

是的,这是一个很强的约束。与堆栈上有如此大的可用空间相比,您最好还是静态地确定。如果代码太小,则使用最新的GCC编译代码,请参见this

顺便说一句,一些便宜的微处理器可能会比“正常”调用帧花费更多的“大”调用帧(例如,因为它们的指令集倾向于从堆栈指针偏移一个字节)。YMMV。

另外,如果您使用C语言进行编码,并且您认为大型数组可以将其空间重用于其他目的,则可以考虑将其设为联合成员(具有union类型为global的变量)。是的,这很丑陋。

另外,您可以考虑对一些适合您的应用程序的原始堆分配器进行编码(它的API可能malloc与&free.... 不同)。


1
谢谢,我实际上有点期待这种答案,即保持静态分配,以确保不会导致堆栈溢出。不幸的是,我没有最新的GCC编译器,代码也不小。
蒂姆(Tim)

您是否无法(可能是通过从其源代码编译GCC)获得适用于您平台的GCC编译器?
巴西尔·斯塔林凯维奇

2
我已经有一个,它真的很旧。从事新工作不在我的范围内,也不适合我的日程安排。但是可以肯定的是,使用最新的工具会使某些事情变得更容易。
蒂姆(Tim)

3
要求老板让您获得更新的GCC(通过为您提供最新的二进制工具,或者让您有时间重新编译GCC)是恕我直言的,因为更新的GCC可能会稍微好一些(您知道gcc -flto -Os吗? ),你可能会得到一些记忆....
巴西莱Starynkevitch

2
它正在按计划进行,但不久后将不会实现。我已经在具有较新工具链的其他系统上使用-O,但是我不知道此-flto参数,谢谢指出。(请注意,几周前我已经在GCC 5.4.1上使用GCC -O和-O1-3参数进行了一些测试,并且RAM一直都是相同的。但是FLASH和MCU的运行时间是不同的。那与我在本次问答中提到的那个MCU不在同一个MCU上)
Tim

6

人们倾向于对大堆栈保持谨慎,因为它在RAM中向后增长并覆盖变量的值,从而导致无法解释的行为。更糟糕的是,因为您需要知道最低的堆栈指针地址,并减去进入例程时要分配的大小。

考虑到硬件存储器管理具有这种分析的功能,这都是硬件存储器管理(当发生堆栈溢出时应生成陷阱或错误)或编译器的工作。

否则,您可以使用RAM进行所需的操作。


4

正如前面的答案所指出的,如果适合内存,我也首先建议将数组保持静态。在大多数情况下,具有确定性的内存占用量更为重要,即使这意味着您要“浪费”内存以用于一直未使用的变量。将大型阵列放到堆栈中会很容易将其炸毁,并且堆栈溢出往往会导致难以发现和难以重现的问题(如果无法使用MMU保护堆栈)。

与union共享与其他数据共享该块的建议是IMO有效的,尽管如果您在其中找到了错误的变量,也可能导致难以发现的问题。

如果您的内存不足,并且迫切需要创建寿命较短的变量来共享它,那么在将数组移至堆栈之前,我会考虑添加动态内存分配,即使它有其自身的缺点。在这种情况下,这可能不是答案,因为与可用内存相比,该阵列听起来很大。


1

如果您有某种闪存,则可以选择其他方法。您可以通过将数据存储在闪存中并在其中读取和搜索来权衡ram的访问速度。您只需要一次将一个记录加载到ram中。如果您需要能够更新记录,它将稍微复杂一些。您将需要一个分段的磨损均衡机制。我过去已经这样做过,并添加了索引以加快访问速度。


1
我过去也这样做过,但是我的问题更多是关于将其放到堆栈上可能遇到的问题,而不是真正的内存不足的问题。感谢您的回应
蒂姆(Tim)

1

尤其是在使用嵌入式系统时,您希望在编译时发生尽可能多的故障,而在运行时则不会发生任何故障(不过,如果我们能够做到这一点,那就太好了……)。

在静态分配的程序的任意状态下制作可能需要的大数组确实做到了-链接器最终会警告您“这不适合RAM”,而堆栈分配只会使程序崩溃并难以调试溢出。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.