7

我希望在 C 中为内存受限的微控制器实现堆分配算法。我已将搜索范围缩小到我知道的 2 个选项,但是我非常愿意接受建议,并且我正在寻找任何有这方面经验的人的建议或意见。

我的要求:

-速度肯定很重要,但它是次要问题。

- 时序确定性并不重要——任何需要确定性最坏情况时序的代码部分都有自己的分配方法。

- 主要要求是碎片免疫。该设备正在运行一个 lua 脚本引擎,这将需要一系列分配大小(在 32 字节块上很重)。主要要求是该设备能够长时间运行而不会将其堆搅成不可用状态。

另请注意:

-作为参考,我们谈论的是 Cortex-M 和 PIC32 部件,内存范围从 128K 到 16MB 或内存(重点放在低端)。

-我不想使用编译器的堆,因为 1)我希望所有编译器的性能保持一致,以及 2)它们的实现通常非常简单,并且对于碎片来说相同或更糟。

- 由于我不想从根本上更改和重新验证庞大的 Lua 代码库,双重间接选项已被淘汰。

到目前为止我最喜欢的方法:

1)有一个二进制伙伴分配器,并牺牲内存使用效率(四舍五入到2的幂)。- 这(据我所知)需要一个二叉树为每个订单/箱存储按内存地址排序的空闲节点,以便快速查找伙伴块以进行重新链接。

2) 有两棵用于空闲块的二叉树,一棵按大小排序,一棵按内存地址排序。(所有二叉树链接都存储在块本身中) - 分配将最适合使用按大小查找表,然后按地址从另一棵树中删除该块 - 解除分配将按地址查找相邻块以进行重新链接

- 两种算法还需要在分配块开始之前存储分配大小,并让块以 2 减 4 的幂(或 8 取决于对齐方式)的形式出现。(除非他们在其他地方存储一棵二叉树来跟踪按内存地址排序的分配,我认为这不是一个好的选择)

- 两种算法都需要高度平衡的二叉树代码。

-算法 2 不要求通过四舍五入到 2 的幂来浪费内存。

-在任何一种情况下,我可能都会有一个固定的 32 字节块组,由嵌套位字段分配,以卸载这个大小或更小的块,这将不受外部碎片的影响。

我的问题:

- 有什么理由说明方法 1 比方法 2 更不受碎片的影响?

- 有没有我遗漏的可能符合要求的替代方案?

4

2 回答 2

1

如果块大小没有四舍五入到 2 的幂或某个等值 (*),则某些分配和解除分配序列将产生基本上无限量的碎片,即使在任何给定时间存在的非永久小对象的数量是有限的。二进制伙伴分配器当然会避免该特定问题。否则,如果使用有限数量的良好相关对象大小但不使用“二进制伙伴”系统,则可能仍然需要使用一些判断来决定在哪里分配新块。

另一种需要考虑的方法是对预期为永久、临时或半永久的事物采用不同的分配方法。当临时和永久的东西在堆上交错时,碎片通常会造成最大的麻烦。避免这种交错可以使碎片最小化。

最后,我知道您并不是真的想使用双间接指针,但是允许对象重定位可以大大减少与碎片相关的问题。许多源自微软的微型计算机 BASIC 使用垃圾收集字符串堆;微软的垃圾收集器真的很糟糕,但它的字符串堆方法可以与一个好的方法一起使用。

于 2012-05-29T19:29:36.587 回答
1

您可以在http://www.mcdowella.demon.co.uk/buddy.html上选择一个(从未真正使用过的)Buddy 系统分配器,我会祝福您用于任何您喜欢的目的。但我不认为你有一个仅仅通过插入内存分配器就可以轻松解决的问题。我熟悉的长期运行的高完整性系统具有可预测的资源使用情况,在每个资源的 30 多页文档中进行了描述(主要是 cpu 和 I/O 总线带宽 - 内存很容易,因为它们倾向于在每次启动时分配相同的数量然后再也不会)。

在您的情况下,没有任何通常的技巧 - 静态分配、空闲列表、堆栈上的分配,可以证明是有效的,因为 - 至少正如我们所描述的 - 你有一个 Lua 解释在后台悬停准备做什么谁知道在做什么运行时——如果它只是进入一个分配内存的循环直到它用完怎么办?

您能否将内存使用分为两部分 - 传统代码在启动时分配几乎所有它需要的东西,并且永远不会再分配,而可消耗代码(例如 Lua)允许在需要时分配它需要的任何东西,从之后剩下的任何东西静态分配?如果消耗性代码设法使用其所有内存区域或对其进行分段,而不打扰传统代码,您是否可以触发重新启动或某种形式的清理?

于 2012-05-30T05:38:14.680 回答