【问题标题】:What algorithm to apply for continuious reallocation of small memory chunks?什么算法申请连续分配小内存块?
【发布时间】:2018-11-16 06:24:29
【问题描述】:

在 C 程序中,我面临需要大量内存块的事务,我需要知道是否有用于处理所有这些 malloc/free 的算法或最佳实践 teqnique,我使用数组来存储这些内存块但是在某些时候数组本身会变满,重新分配数组只会更浪费,处理这个问题的优雅方法是什么?

【问题讨论】:

  • 是什么让您认为重新分配数组是浪费?见this answer后半部分
  • 此数组重新分配是否会在您的应用程序中导致可衡量的性能问题?

标签: c memory memory-management dynamic-memory-allocation memory-fragmentation


【解决方案1】:

这种情况下最好的算法是free list分配器+二叉搜索树。

您正在向系统请求一个大内存块,然后从该块分配固定大小的内存块。当块已满时,您将分配另一个块,并将它们链接到红黑或 AVL 二进制搜索间隔树(否则在 free 期间通过遍历块列表来搜索块会成为瓶颈)

同时,多线程和线程同步变得很重要。如果您只使用互斥锁或普通自旋锁,您会发现 libc malloc 的工作速度比您自定义的内存分配器快得多。解决方案可以在Hazard pointer 中找到,即每个线程都有自己的内存分配器(或每个 CPU 内核一个分配器)。这将增加另一个问题 - 当一个线程分配内存而另一个释放它时,这将需要在空闲函数期间搜索确切的分配器,并严格锁定或无锁数据结构。

最佳选择 - 您可以使用 jemalloctcmalloc 或任何其他通用建议快速内存分配器来完全或部分替换您的 libc(即 pdmalloc)默认分配器。

【讨论】:

    【解决方案2】:

    如果您因为要交付的功能而需要跟踪这些不同的缓冲区,那么您将需要某种缓冲区管理功能,其中包括为缓冲区管理分配内存。

    但是,如果您担心内存碎片,那就另当别论了。

    我已经看到很多关于使用malloc()free() 如何导致内存碎片以及减少或消除此问题的各种方法的文章。

    我怀疑几年前,这可能是个问题。然而,这些天来,我质疑大多数程序员是否可以像开发现代编译器运行时的人一样做好内存管理工作。我确信有一些特殊的应用程序,但编译器运行时开发非常了解内存碎片,并且他们使用了许多方法来帮助缓解问题。

    例如,这是 2010 年的一篇较早的文章,描述了现代编译器运行时实现 malloc()free()A look at how malloc works on the Mac

    这里有一点关于GNU allocator

    IBM 于 2004 年 11 月发表的这篇文章描述了内存管理的一些注意事项,Inside memory management 并提供了他们所谓的“malloc 的简单实现代码,并免费帮助演示管理内存所涉及的内容”。请注意,这是一个简单的示例,旨在说明一些问题,而不是当前实践的演示。

    我使用 Visual Studio 2015 做了一个快速控制台应用程序,它在 C 源文件中调用了一个 C 函数,其中穿插了各种大小的 malloc()free() 调用。我在 Windows 任务管理器中观察进程时运行了这个。峰值工作集(内存)最大为 34MB。观察内存(私有工作集)测量,我看到它随着程序运行而上升和下降。

    #include <malloc.h>
    #include <stdio.h>
    
    void funcAlloc(void)
    {
        char *p[50000] = { 0 };
        int   i;
    
        for (i = 0; i < 50000; i+= 2) {
            p[i] = malloc(32 + (i % 1000));
        }
    
        for (i = 0; i < 50000; i += 2) {
            free(p[i]); p[i] = 0;
        }
        for (i = 1; i < 50000; i += 2) {
            p[i] = malloc(32 + (i % 1000));
        }
        for (i = 1; i < 50000; i += 2) {
            free(p[i]); p[i] = 0;
        }
    
        for (i = 0; i < 50000; i++) {
            p[i] = malloc(32 + (i % 1000));
        }
    
        for (i = 0; i < 50000; i += 3) {
            free(p[i]); p[i] = 0;
        }
        for (i = 1; i < 50000; i += 3) {
            free(p[i]); p[i] = 0;
        }
        for (i = 2; i < 50000; i += 3) {
            free(p[i]); p[i] = 0;
        }
    }
    
    void funcMain(void)
    {
        for (int i = 0; i < 5000; i++) {
            funcAlloc();
        }
    }
    

    在使用malloc()free() 搅动内存的某些情况下,程序员应该练习帮助内存分配器的唯一考虑是使用一组标准缓冲区大小来处理不同长度的数据。

    例如,如果您要为几个不同大小的不同数据结构创建临时缓冲区,请为所有缓冲区使用最大 struct 的标准缓冲区大小,以便内存管理器使用相同大小的块内存,因此它能够更有效地重用空闲内存块。我已经看到一些动态数据结构功能使用这种方法,例如分配最小长度为 32 个字符的动态字符串或将缓冲区请求四舍五入为四或八的倍数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-02
      • 2014-10-29
      • 2011-01-15
      • 2010-10-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多