我希望在C中为内存受限的微控制器实现堆分配算法。我已将搜索范围缩小到我所知道的2个选项,但我对建议非常开放,我正在寻找有经验的人的建议或意见。
我的要求:
-Speed绝对有意义,但是次要问题。
- 定时确定性并不重要 - 需要确定性最坏情况时序的代码的任何部分都有自己的分配方法。
- MAIN要求是碎片免疫力。该设备正在运行lua脚本引擎,这将需要一系列分配大小(32字节块上很重)。主要要求是该设备运行很长时间而不会将其堆转变为不可用状态。
另请注意:
- 作为参考,我们讨论的是Cortex-M和PIC32部件,内存范围从128K到16MB或内存(重点放在低端)。
- 我不想使用编译器的堆,因为1)我想在所有编译器中保持一致的性能,2)它们的实现通常非常简单,对于碎片来说是相同或更差。
- 双重间接选项因为巨大的Lua代码库而无法进行资金更改和重新验证。
我最喜欢的方法:
1)拥有二进制伙伴分配器,并牺牲内存使用效率(向上舍入为2大小的功率)。 - 这会(据我所知)需要为每个订单/ bin提供一个二叉树来存储按内存地址排序的空闲节点,以便进行快速伙伴块查找以进行重新链接。
2)有两个二进制树用于空闲块,一个按大小排序,一个按内存地址排序。 (所有二叉树链接都存储在块本身中) -allocation最适合使用按大小查找表,然后按地址从另一个树中删除该块 -deallocation将按地址查找相邻块以进行重新链接
- 两个算法还需要在分配块的开始之前存储分配大小,并且具有作为2减去4(或8取决于对齐)的幂的块。 (除非他们将二叉树存储在别处以跟踪按内存地址排序的分配,我认为这不是一个好选择)
- 两种算法都需要高度平衡的二叉树代码。
- 算法2没有要求通过舍入到2的幂来浪费内存。
- 在任何一种情况下,我都可能有一个由嵌套位字段分配的32字节块的固定存储区,用于卸载此大小或更小的块,这将不受外部碎片的影响。
我的问题:
- 有没有理由为什么方法1对于碎片比对方法2更有免疫力?
- 我是否有任何可能符合要求的替代品?
答案 0 :(得分:1)
如果块大小没有四舍五入到2或某个等价(*)的幂,则某些分配和释放序列将产生基本上无限制的碎片量,即使任何存在于其中的非永久性小对象的数量也是如此给定时间有限。当然,二元伙伴分配器将避免该特定问题。否则,如果一个人使用的是有限数量的相关对象大小但没有使用“二元伙伴”系统,那么可能仍然需要使用一些判断来决定在哪里分配新块。
另一种需要考虑的方法是为预期为永久性,临时性或半持久性的事物提供不同的分配方法。当临时和永久事物在堆上交错时,碎片通常会导致最大的麻烦。避免这种交错可以最大限度地减少碎片。
最后,我知道你真的不想使用双重间接指针,但允许对象重定位可以大大减少与碎片相关的问题。许多微软派生的微型计算机BASIC使用垃圾收集的字符串堆;微软的垃圾收集器非常糟糕,但它的字符串堆方法可以用得很好。
答案 1 :(得分:1)
你可以在http://www.mcdowella.demon.co.uk/buddy.html选择一个(从未使用过真正的)Buddy系统分配器,我的祝福可以用于任何你喜欢的目的。但我不认为你有一个问题,只需插入内存分配器即可轻松解决。我熟悉的长期运行的高完整性系统具有可预测的资源使用,在每个资源的30多页文档中描述(主要是cpu和I / O总线带宽 - 内存很容易,因为它们往往在启动时分配相同的数量然后再也不会。)
在你的情况下,通常的技巧 - 静态分配,自由列表,堆栈上的分配都没有显示出来 - 因为 - 至少就像我们所描述的那样 - 你有一个Lua解释在后台徘徊准备做谁知道什么是运行时 - 如果它只是进入一个循环分配内存,直到它用完为止?
你能否将内存使用分为两个部分 - 传统代码几乎分配了它在启动时所需的全部内容,而且再也没有,并且可消耗代码(例如Lua)允许在需要时分配它需要的任何东西,无论是什么静态分配后遗留下来?如果它设法使用其所有内存区域或将其分段,而不打扰传统代码,您是否可以触发重新启动或某种类型的可消耗代码清理?