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

标签 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)的调用结果回显到已定义的变量名中?最好没有单独的配置脚本。

最佳答案

我编写的代码的工作方式类似于您在讨论的内容,但是我并未尝试使用预处理器宏。

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

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

出于我的目的,程序的不同部分可以共享对象,因此我对每个对象都有一个引用计数。 ReleaseObject()将减少引用计数,当它变为零时,会将对象放在适当的就绪列表中。
GetObject()返回的句柄实际上只是指向链表结构的指针。

在我的代码中,如果存在对GetObject()的调用,并且就绪列表为空,则会自动调用malloc()并创建并返回一个新对象。 register函数需要一个指向要使用malloc()创建对象的函数的指针,一个指向要使用free()释放对象的函数的指针以及一个指向“健全性检查”函数的指针(因为我喜欢我的程序可以在调用assert()的运行时),以及任何类似对象大小的参数。

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

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

我在程序中发现的最常见的错误:完成对象后无法调用ReleaseObject()。然后,该程序发生内存泄漏,并频繁调用malloc(),并且在嵌入式平台上内存不足并死亡。通常,很容易注意到这一点,并在ReleaseObject()中添加适当的调用。

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

这意味着我的“注册”函数在被管理的各种不同种类的对象中的数量为O(N),但是对于我的应用程序,只有大约4种不同种类的对象,因此我从未尝试对其进行优化。我的“get”函数是O(1),因为它具有指向正确的对象管理器结构的指针,并且从链接列表的开头删除是恒定时间操作。

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

在我的应用程序中,我不需要“取消注册”操作,但是如果有的话,它将涉及释放该就绪列表中的所有对象,并将该对象管理器阵列中的该位置标记为未使用。

我的应用程序是音频处理引擎,它在处理音频时从不希望调用malloc(),因为malloc()可能会决定重新组织空闲块列表或其他内容,并花一些时间,并且音频播放可能会出现故障。在开始播放之前,引擎具有“初始化”阶段,其中代码调用“寄存器”功能,并且所有音频缓冲区都已分配;然后在运行时,缓冲区会飞走并进入准备就绪列表。它确实运行良好,并且我已经在低功耗DSP芯片上运行了它,没有任何问题。

关于c - 实现各种大小的结构的基于数组的内存池,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19665092/

相关文章:

c - 为什么函数不应该返回本地数组?

c - 在c中用数组作为变量的结构

c - 缺少号码

使用指针发送数组的C项目

c - 数据包的协议(protocol)

c++ - 预处理器宏覆盖 C++ 中的函数定义

java - Java : how to change the path for an imported class 中的预编译器开关

oracle10g - Oracle10g 的 C 预编译器位于哪里?

c - 如何在Mac上使用Oracle预编译器