实现各种大小的结构的基于数组的内存池

时间:2013-10-29 17:22:51

标签: c precompiler

我正在使用一个使用许多小型临时对象的并发数据结构。很多这些对象的大小相同。因此,为了减少内存分配器的压力,我一直使用线程局部映射将对象存储为<size, list>元组。

当线程需要一个对象时,它会在转到分配器之前检查映射是否有合适的对象。这种方法运行良好并且性能显着提高,但是它很容易出现超时一个线程将其整个池丢失到其他线程,迫使它分配新对象的问题。如果应用程序运行了很长一段时间,我会发现一些线程的内存池很大。

要解决此问题,我想在线程本地池和分配器之间添加共享内存池。由于结构的数量和结构的大小在编译时是恒定的,我认为应该使用宏来将每个大小映射到数组位置。允许更轻松的内存管理。

这是我目前的解决方案

#define RC_OBJECT_COUNT 0
#define ADD_RC_OBJECT(object) \
    #ifndef RC_MAP_``sizeof(object)''
        #define RC_OBJECT_TEMP RC_OBJECT_COUNT \
        #undefine RC_OBJECT_COUNT \
        #define RC_OBJECT_COUNT RC_OBJECT_TEMP+1 \
        #define RC_MAP_``sizeof(object)'' RC_OBJECT_TEMP
    #endif

有没有办法将sizeof(object)调用的结果回显到定义的变量名中?最好没有单独的配置脚本。

1 个答案:

答案 0 :(得分:0)

我编写的代码与您正在讨论的内容类似,但我没有按照您尝试的方式使用预处理器宏。

我的代码只有一个“对象管理器”,带有一个小API。我的程序中任何想要获取对象的部分都调用API函数,即“寄存器函数”,它表示“我想要请求具有以下特征的对象”。 register函数返回一个句柄。然后,有一个函数GetObject(),它将句柄作为参数,并返回一个指向对象的指针。当代码完成对象时,有一个函数ReleaseObject(),它接受​​一个指向对象的指针。

对于每个不同的对象,都有一个链接列表,我称之为“就绪列表”。代码总是从列表的头部插入和删除(因为一个未初始化的对象和另一个一样好;它们的大小都相同)。我的代码是单线程的,所以我没有任何锁定问题,但对于多线程我需要锁定每个就绪列表。 (但是在链表的头部插入或删除它非常快,因此没有线程需要锁定很长时间。)

就我的目的而言,程序的不同部分可以共享对象,所以我对每个对象都有一个引用计数。 ReleaseObject()会减少引用计数,当它变为零时,会将对象放在相应的就绪列表中。

GetObject()返回的句柄实际上只是指向链表结构的指针。

在我的代码中,如果调用GetObject()并且就绪列表为空,则会自动调用malloc()并创建并返回一个新对象。 register函数接受一个指向函数的指针,该函数用于调用以创建具有malloc()的对象,指向用free()释放对象的函数的指针,以及指向“完整性检查”函数的指针(从我喜欢我的程序在运行时通过调用assert())以及任何参数(例如对象的大小)来检查自己。

如果我的程序的多个部分注册了他们想要相同类型的对象,则对象管理器会注意到这一点,并且只返回已经由第一次调用注册设置的就绪列表的句柄。现在,他们在一个就绪列表中共享对象。

这可能听起来很复杂但是我不需要花费很长时间才能构建它并且效果非常好。如果就绪列表中没有对象,则对象管理器知道只调用存储在就绪列表结构中的函数指针,以获取一个新对象,然后返回该对象。

我在程序中发现的最常见错误:完成对象后未能调用ReleaseObject()。然后程序有内存泄漏并且调用malloc()很多,并且在嵌入式平台上耗尽内存并死掉。通常很容易注意到这一点,并添加适当的ReleaseObject()调用。

编辑:(回复评论中的问题)对象管理器保留一组不同的对象管理结构实例。每个struct存储三个函数指针:指向“new”函数的指针,指向“delete”函数的指针,指向“sanity check”函数的指针;调用时传递给“new”函数的一些值(例如,所需缓冲区的大小);和链接的对象列表的头部。当代码调用“register”函数时,对象管理器检查此数组中的任何一个点是否具有与“register”(3个函数指针和少量值)相同的值。如果找到相同的值,则对象管理器返回指向该对象管理器结构实例的指针。如果找不到相同的值,则对象管理器将这些值复制到数组中的下一个可用结构中,并返回指向该结构的指针。

这意味着我的“注册”功能在被管理的不同类型对象的数量上是O(N),但对于我的应用程序,只有大约4种不同类型的对象,所以我从未尝试过优化它。我的“get”函数是O(1),因为它有一个指向正确的对象管理器结构的指针,并且从链表的头部删除是一个恒定时间操作。

对象管理器结构数组由malloc()分配,如果注册了其他对象类型,代码可以调用realloc()来增加内存。

在我的应用程序中,我没有需要“取消注册”操作,但如果有一个,它将涉及释放该就绪列表上的所有对象,并将对象管理器数组中的那个点标记为未使用

我的应用是一个音频处理引擎,它永远不想在处理音频时调用malloc(),因为malloc()可能会决定重新组织空闲阻止列表或其他东西,并且需要一段时间,音频播放可能会出现故障。在开始播放之前引擎具有“初始化”阶段,其中代码调用“寄存器”功能并且所有音频缓冲器都被分配;然后在运行时缓冲区就会飞离并进入就绪列表。它确实运行得很好,我已经在低功耗DSP芯片上运行它没有问题。