我有一个单线程嵌入式应用程序,可以分配和释放大量的小块(32-64b)。基于缓存的分配器的完美方案。虽然我可以尝试写一个,但这可能是浪费时间,而不是像一些已经在前线的解决方案那样经过测试和调整。
那么我可以用于这种情况的最佳分配器是什么?
注意:我在系统中使用Lua虚拟机(这是80%以上的分配的罪魁祸首),所以我不能轻易地重构我的代码以使用堆栈分配来提高分配性能。
答案 0 :(得分:9)
在我过去的C项目中,我们为在包括嵌入式系统在内的各种平台上运行的库实现我们自己的内存管理例程。该库还分配并释放了大量小缓冲区。它运行得相对较好,并没有采用大量代码来实现。如果你想自己开发一些东西,我可以给你一些关于该实现的背景知识。
基本实现包括一组管理设置大小的缓冲区的例程。这些例程被用作malloc()和free()的包装器。我们使用这些例程来管理我们经常使用的结构的分配,以及管理集合大小的通用缓冲区。结构用于描述被管理的每种类型的缓冲区。当分配了特定类型的缓冲区时,我们将malloc()块中的内存(如果空闲缓冲区列表为空)。 IE,如果我们管理10个字节的缓冲区,我们可能会生成一个malloc(),其中包含100个这些缓冲区的空间,以减少碎片和所需的底层malloc数量。
在每个缓冲区的前面将是一个指针,用于链接空闲列表中的缓冲区。当分配100个缓冲区时,每个缓冲区将在空闲列表中链接在一起。使用缓冲区时,指针将设置为null。我们还维护了缓冲区“块”的列表,这样我们就可以通过在每个实际的malloc缓冲区上调用free()来进行简单的清理。
为了管理动态缓冲区大小,我们还在每个缓冲区的开头添加了一个size_t变量,告诉缓冲区的大小。然后使用它来识别哪个缓冲区块在释放时将缓冲区放回。我们有malloc()和free()的替换例程,它们使用指针算法来获取缓冲区大小,然后将缓冲区放入空闲列表中。我们对我们管理的缓冲区数量也有限制。大于此限制的缓冲区只是malloc'd并传递给用户。对于我们管理的结构,我们创建了用于分配和释放特定结构的包装程序。
最终,我们还将系统演变为在用户请求清理未使用的内存时包含垃圾收集。由于我们可以控制整个系统,因此我们可以随着时间的推移进行各种优化以提高系统性能。正如我所提到的,它确实运作良好。
答案 1 :(得分:8)
我最近对这个话题进行了一些研究,因为我们遇到了内存碎片问题。最后,我们决定继续使用GNU libc的实现,并在必要时添加一些应用程序级内存池。还有其他分配器具有更好的碎片行为,但是我们对它们的全球替换malloc不够舒服。 GNU的背后有着悠久的历史。
在你的情况下,它似乎是合理的;假设您无法修复VM,那些微小的分配非常浪费。我不知道你的整个环境是什么,但你可以考虑在VM上包含对malloc / realloc / free的调用,这样你就可以把它传递给专为小池设计的处理程序。
答案 2 :(得分:8)
我参加派对有点晚了,但我只是想为我最近发现和测试的嵌入式系统分享非常有效的内存分配器:https://github.com/dimonomid/umm_malloc
这是一个专门设计用于ARM7的内存管理库,我个人在PIC32设备上使用它,但它应该适用于任何16位和8位器件(我计划在16位PIC24上测试) ,但我还没有测试过它)
我被默认分配器的碎片严重打败了:我的项目经常分配各种大小的块,从几个字节到几百个字节,有时候我会遇到“内存不足”的问题。错误。我的PIC32器件总共有32K的RAM,而8192字节用于堆。在特定时刻,有超过5K的可用内存,但由于碎片,默认分配器具有最大的非碎片内存块,大约只有700字节。这太糟糕了,所以我决定寻找更有效的解决方案。
我已经知道了一些分配器,但它们都有一些限制(例如块大小应该是2或2,而不是从2开始,而是从128字节开始),或者只是错误。之前,我不得不切换回默认的分配器。
但这一次,我很幸运:我找到了这个:http://hempeldesigngroup.com/embedded/stories/memorymanager/
当我尝试这个内存分配器时,在与5K可用内存完全相同的情况下,它有超过3800字节块!这对我来说是难以置信的(相比700字节),我进行了严格的测试:设备工作时间超过30小时。没有内存泄漏,一切正常,因为它应该工作。 我还在FreeRTOS存储库中找到了这个分配器:http://svnmios.midibox.org/listing.php?repname=svn.mios32&path=%2Ftrunk%2FFreeRTOS%2FSource%2Fportable%2FMemMang%2F&rev=1041&peg=1041#,这个事实是umm_malloc稳定性的另一个证据。 所以我完全切换到了umm_malloc,我对它很满意。
我只需更改一下:当未定义宏UMM_TEST_MAIN时,配置有点错误,因此,我已经创建了github存储库(链接位于此帖子的顶部)。现在,用户相关配置存储在单独的文件umm_malloc_cfg.h
中我还没有深入了解在这个分配器中应用的算法,但它对算法有非常详细的解释,所以任何有兴趣的人都可以查看文件的顶部umm_malloc.c。至少," binning"方法应该在较少碎片化方面带来巨大好处:http://g.oswego.edu/dl/html/malloc.html
我相信任何需要为微控制器提供高效内存分配器的人,至少应该试试这个。
答案 3 :(得分:6)
虽然距离我提出这个问题还有一段时间,但我最后的解决方案是使用LoKi的SmallObjectAllocator它非常有效。摆脱了所有操作系统调用,提高了我的嵌入式设备Lua引擎的性能。非常好,简单,只需要大约5分钟的工作量!
答案 4 :(得分:5)
答案 5 :(得分:3)
我还想加入这个,即使它是一个旧线程。在嵌入式应用程序中,如果您可以分析应用程序的内存使用情况并提出不同大小的最大内存分配数,通常最快的分配器类型是使用内存池的分配器。在我们的嵌入式应用程序中,我们可以确定运行时期间所需的所有分配大小。如果您可以这样做,您可以完全消除堆碎片并具有非常快速的分配。大多数这些实现都有一个溢出池,它将为特殊情况做一个常规的malloc,如果你正确地进行分析,它们之间的距离很小。
答案 6 :(得分:2)
我在vxworks下使用'binary buddy'系统效果很好。基本上,你通过将块减半来获得堆,以获得两个大小的块的最小功率来保存您的请求,并且当块被释放时,您可以向上传递树以将块合并回来以减少碎片。谷歌搜索应该会显示您需要的所有信息。
答案 7 :(得分:-1)
我正在编写一个名为tinymem的C内存分配器,旨在对堆进行碎片整理,并重用内存。看看:
https://github.com/vitiral/tinymem
注意:此项目已停止用于执行生锈:
https://github.com/vitiral/defrag-rs
另外,我之前没有听说过umm_malloc。不幸的是,它似乎无法处理碎片,但它看起来确实很有用。我将不得不检查出来。