定制malloc用于许多小型固定大小的块?

时间:2010-09-01 22:43:38

标签: c malloc

我需要分配并释放大量固定大小的小(16字节)内存块,而不是固定的顺序。我可以为每个人调用malloc并释放,但这可能效率很低。一个更好的解决方案可能是调用malloc并释放更大的块,并在这些块本身内处理分配。

问题是,如何最好地做到这一点?

这似乎不应该是一个非常不寻常的问题或罕见的问题,它应该已经“解决”,但我似乎找不到任何东西。有什么指针吗?

为了澄清,我知道内存池库,什么不存在,但这些也需要一个size参数。如果大小是恒定的,那么可以使用更高效算法的不同选项,这些是否有任何实现?

9 个答案:

答案 0 :(得分:5)

你是对的,这是一个常见的问题[编辑:如何进行固定大小的分配,我的意思是。 “malloc正在减慢我的申请速度”并不像你想象的那么常见。“

如果你的代码太慢并且malloc是一个看似合理的罪魁祸首,那么一个简单的单元分配器(或“内存池”)可能会改善一些事情。几乎可以肯定在某个地方找到一个,或者写起来很容易:

分配一个大块,并在每个16字节单元的开头放置一个单链接列表节点。把它们连在一起。要分配,请从列表中删除并返回它。要释放,请将单元格添加到列表的开头。当然,如果你尝试分配并且列表为空,那么你必须分配一个新的大块,将它分成单元格,然后将它们全部添加到空闲列表中。

如果你愿意,你可以避免那种大的前期工作。分配大块时,只需存储指向它结尾的指针。要分配,请将指针移回块中的16个字节并返回新值。当然,除非它已经在块[*]的开头。如果发生这种情况,并且空闲列表也是空的,则需要一个新的大块。 Free不会更改 - 只需将节点添加到空闲列表中。

您可以选择是先退出该区块,如果已用完则检查空闲列表,或先查看空闲列表,如果空白,则处理该区块。我不知道哪个往往更快 - 一个倒数第一的免费列表的好处是它的缓存友好,因为你使用的是最近使用的内存,所以我可能会尝试第一

请注意,在分配单元时不需要列表节点,因此每个单元的开销基本上为零。除了速度之外,这可能比malloc或其他通用分配器更具优势。

请注意,丢弃整个分配器几乎是将内存释放回系统的唯一方法,因此计划分配大量单元,使用它们并将它们全部释放的用户应该创建自己的分配器,使用它,然后销毁它。两者都是为了性能(您不必释放所有单元格)并防止碎片式效果,如果任何单元格正在使用中必须保留整个块。如果你不能这样做,你的记忆使用将是你的程序运行时间的最高标记。对于某些有问题的程序(例如,长时间运行的程序,内存使用偶尔会出现大量峰值,在内存受限的系统上)。对于其他人来说,它绝对没问题(例如,如果使用的细胞数量增加,直到非常接近程序结束,或者在你真正不关心你使用的内存超出你的严格要求的范围内波动)。对于一些它的主动需求(如果你知道你将要使用多少内存,你可以预先分配它,而不必担心失败)。就此而言,malloc的某些实现难以将内存从进程释放回操作系统。

[*]其中“块的开始”可能意味着“块的开始,加上用于维护所有块列表的某个节点的大小,因此当单元分配器被销毁时它们都可以被释放”

答案 1 :(得分:4)

在开始重写malloc的艰巨任务之前,标准建议适用。描述您的代码,并确保这实际上是一个问题!

答案 2 :(得分:4)

这样做的最好方法是不要假设它效率低下。而是尝试使用malloc解决方案,测量性能并证明它有效或无效。然后,一旦提供不足(可能不会)是唯一一次你应该转移到自定义分配器。没有证据,你永远不会知道你的解决方案是否真的更快。

答案 3 :(得分:3)

根据您的要求,您的自定义分配器非常简单。只是calloc一个大型数组内存

calloc(N * 16)

然后你可以分发数组条目。为了跟踪正在使用的数组位置,您可以使用简单的位图,然后使用一些聪明的位操作和指针减法,您的自定义malloc/free操作应该非常简单。如果你的空间不足,你可以realloc更多,但是有一个合适的固定默认值会更容易。

但实际上你应该先使用mallocmalloc创建了不同大小的可用内存块池,我敢打赌,有一个16字节内存块的池(不同的实现可能会也可能不会这样,但它是一个非常常见的优化),因为你的所有分配都是同样大小的碎片应该不是问题。 (加上调试你的分配器可能有点噩梦。)

答案 4 :(得分:2)

您正在寻找的内容称为内存池。有现成的实现,虽然创建自己的实现并不困难(和良好实践)。

对于相同大小的数据池,最简单的实现只是一个包含n *大小的缓冲区和一堆n个指针的包装器。池中的“malloc”将指针从顶部弹出。 “free”到池中会将指针放回堆栈中。

答案 5 :(得分:1)

您可以尝试覆盖适合大量小额分配的malloc / free with an alternative implementation

答案 6 :(得分:1)

由于学术兴趣,我几天前正致力于解决这个问题。实现非常简单但完整,你提到你正在寻找替代品,所以我认为我的实现可能适合你。

基本上它就像描述的patros一样,只是如果没有空闲块就会自动请求更多的内存。该代码使用大型链表(大约6个节点,每个大小为16个字节)与一个天真的malloc()/ free()方案进行测试,执行速度比这快15%。所以据说它可以用于你的意图。由于在创建如此大的内存块时指定的块大小,因此很容易将其调整为不同的块大小。

代码可在github上找到:challoc

使用示例:

int main(int argc, char** argv) {
    struct node {
           int data;
       struct node *next, *prev;
    };
    // reserve memory for a large number of nodes
    // at the moment that's three calls to malloc()
    ChunkAllocator*  nodes = chcreate(1024 * 1024, sizeof(struct node));

    // get some nodes from the buffer
    struct node* head = challoc(nodes);
    head->data = 1;
    struct node* cur = NULL;
    int i;
    // this loop will be fast, since no additional
    // calls to malloc are necessary
    for (i = 1; i < 1024 * 1024; i++) {
            cur = challoc(nodes);
        cur->data = i;
        cur = cur->next;
    }

    // the next call to challoc(nodes) will
    // create a new buffer to hold double
    // the amount of `nodes' currently holds

    // do something with a few nodes here

    // put a single node back into the buffer
    chfree(nodes,head);

    // mark the complete buffer as `empty'
    // this also affects any additional
    // buffers that have been created implicitly
    chclear(nodes);

    // give all memory back to the OS
    chdestroy(nodes);

    return 0;
}

答案 7 :(得分:0)

Wilson,Johnstone,Neely和Boles写道a nice paper surveying all sorts of different allocators

根据我的经验,良好的固定池分配器和仅仅依赖于dlmalloc之间的性能和开销差异可能是大量的,如果您正在进行大量的短期小分配有限的地址空间(例如没有页面文件的系统)。在我正在研究的应用程序中,如果我用简单的malloc()调用替换我们的块分配器,我们的主循环从30ms跳到> 100ms(并且它最终因碎片而崩溃)。

答案 8 :(得分:0)

以下代码非常难看,但目的不是美观,而是要找出malloc分配的块有多大。
我要求4个字节,并且malloc请求并从OS接收135160个字节。

#include <stdio.h>
#include <malloc.h>


int main()
{
  int* mem = (int*) malloc( sizeof(int) ) ;
  if(mem == 0) return 1;
  long i=1L;

  while(i)
    {
      mem[i-1] = i;
      printf("block is %d bytes\n", sizeof(int) * i++);
    }//while

  free(mem);
  return 0 ;
}

$ g ++ -o file file.cpp
$ ./file
...
块是135144字节
块是135148字节
块是135152字节
块是135156字节
块是135160字节
分段错误

这个malloc是一个严肃的事业 如果请求的大小小于内部池的可用大小,则realloc不会进行任何系统调用 在realloc将内存复制到更大的区域后,它也会破坏前一个块,而不是立即将其返回给系统。这仍然可以访问(当然完全不安全)。 有了这些,对我来说没有意义,有人需要一个额外的内存池。