【问题标题】:Efficient allocation of dynamic arrays within mmap'ed memory在 mmap 内存中有效分配动态数组
【发布时间】:2012-10-19 19:35:03
【问题描述】:

我有一个非常大(在运行时固定,大约 10 到 3000 万)的数组。每个数组包含 0 到 128 个元素,每个元素为 6 个字节。

我需要将所有数组存储在 mmap 内存中(所以我不能使用 malloc),并且数组需要能够动态增长(最多 128 个元素,并且数组永远不会缩小)。

我实现了一种简单的方法,即使用一个 int 数组来表示 mmap 内存中每个 6 字节块的状态。偏移量处的值 0xffffffff 表示 mmap 内存中的相应偏移量是空闲的,任何其他值都是数组的 id(在我当前的实现中对块进行碎片整理需要它,没有就不能移动块知道他们的数组的 id 来更新其他数据结构)。在分配时,当数组超出其分配时,它会简单地迭代,直到找到足够的空闲块,然后在相应的偏移量处插入。

这是分配数组和映射内存的样子,有点:

| 0xffffffff | 0xfffffff |    1234    |    1234    | 0xffffffff | ...
-----------------------------------------------------------------
|    free    |   free    |array1234[0]|array1234[1]|    free    | ...


这种方法虽然有 offset of furthest used block in mmap'ed memory x 4(4 字节 ber int)的内存开销。

对于这种特定情况有什么更好的方法?

我对此的理想要求是:

  • 内存开销(任何分配表 + 未使用空间)
  • O(1) 数组的分配和增长

【问题讨论】:

  • 您能否确认:您使用的是 Linux 或其他类似 POSIX 的系统?您没有使用 MS Windows?
  • @Robᵩ 是的,最新的 Ubuntu Linux

标签: c++ memory-management malloc


【解决方案1】:

Boost.Interprocess 似乎有一个简洁的managed memory-mapped files 实现,其规定类似于 malloc/free 但用于映射文件(即您有一个适当大的内存映射文件的句柄,您可以要求库为某些东西(例如数组)子分配文件的未使用部分)。来自文档:

Boost.Interprocess 提供了一些创建共享内存的基本类 对象和文件映射并将那些可映射的类映射到 进程的地址空间。

但是,管理这些内存段并不容易 不平凡的任务。映射区域是一个固定长度的内存缓冲区,并且 动态创建和销毁任何类型的对象,需要 很多工作,因为它需要编程内存管理 算法来分配该段的部分。很多时候,我们也 想要将名称关联到在共享内存中创建的对象,所以所有 进程可以使用名称找到对象。

Boost.Interprocess 提供 4 个托管内存段类:

  • 管理共享内存映射区域(basic_managed_shared_memory 类)。
  • 管理内存映射文件 (basic_managed_mapped_file)。
  • 管理堆分配的(operator new)内存缓冲区(basic_managed_heap_memory 类)。
  • 管理用户提供的固定大小缓冲区(basic_managed_external_buffer 类)。

托管内存段最重要的服务是:

  • 内存部分的动态分配。
  • 在内存段中构造 C++ 对象。这些对象可以是匿名的,或者我们可以为它们关联一个名称。
  • 命名对象的搜索功能。
  • 许多功能的自定义:内存分配算法、索引类型或字符类型。
  • 原子构造和破坏,因此如果段在两个进程之间共享,则不可能创建两个对象 与同名关联,简化同步。

【讨论】:

    【解决方案2】:

    您可以负担多少个映射区域?如果 128 没问题,那么我会创建 128 个区域,这些区域对应于您的数组的所有可能大小。理想情况下,每个区域都有一个免费条目的链接列表。在这种情况下,您将获得每个区域的固定大小的记录。并且将数组从 N 增长到 N + 1 是将数据从 area[N] 移动到最后的 area[N + 1] 的操作(如果 N + 1 的空条目的链表为空)或在如果没有空槽。对于 area[N],删除的插槽被添加到其空闲条目列表中

    更新:链表可以嵌入到主要结构中。因此不需要额外的分配,每个可能的记录(从大小 1 到 128)中的第一个字段 (int) 可以是下一个空闲条目的索引。对于分配的条目,它总是无效的(0xffffffff),但如果一个条目是空闲的,那么这个索引就成为相应链接链的成员。

    【讨论】:

    • 不幸的是,由于耐久性要求,我认为我只能使用 1 个 mmap'ed 文件 - 我认为必须 msync > 1 个文件会引入崩溃可能损坏数据的间隙,并且我认为围绕它进行编码麻烦多于其价值。
    【解决方案3】:

    我设计并最终采用了一种几乎符合我要求的内存分配算法,具有 O(1) 摊销、很少的碎片和很少的开销。欢迎发表评论,有机会我会详细介绍。

    【讨论】:

    • 你的算法听起来很有趣。你能分享细节或代码吗?
    • 9 年后,它仍然听起来很有趣。喜欢听更多。不过,就目前而言,您可能不应该将此标记为“已接受的答案”。即使你自己得到了答案,社区还没有。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-07
    • 2014-12-09
    • 2016-06-03
    • 2021-07-17
    • 2013-05-24
    • 2013-01-27
    相关资源
    最近更新 更多