如何在C中编写线程安全、高效、无锁的内存分配器?

如何在C中编写线程安全、高效、无锁的内存分配器?,c,multithreading,memory,performance,allocation,C,Multithreading,Memory,Performance,Allocation,如何在C中编写线程安全、高效、无锁的内存分配器?我所说的高效是指: 快速分配和释放 最佳内存使用率(最小浪费和无外部碎片) 最小元数据开销 这取决于你所说的效率。如果我关心的是让事情变得更快,那么我可能会给每个线程提供它自己的独立内存池,以及一个从该内存池中获取内存的自定义“malloc”。当然,如果我关心的是速度,我可能会首先避免分配 没有一个答案;你将平衡一系列的顾虑。要获得一个无锁的分配器几乎是不可能的,但是您可以提前且不频繁地进行锁定(通过为每个线程分配大的池),或者将锁设置得非常小和紧

如何在C中编写线程安全、高效、无锁的内存分配器?我所说的高效是指:

  • 快速分配和释放

  • 最佳内存使用率(最小浪费和无外部碎片)

  • 最小元数据开销


  • 这取决于你所说的效率。如果我关心的是让事情变得更快,那么我可能会给每个线程提供它自己的独立内存池,以及一个从该内存池中获取内存的自定义“malloc”。当然,如果我关心的是速度,我可能会首先避免分配

    没有一个答案;你将平衡一系列的顾虑。要获得一个无锁的分配器几乎是不可能的,但是您可以提前且不频繁地进行锁定(通过为每个线程分配大的池),或者将锁设置得非常小和紧凑,以确保它们是正确的。

    本文提出了一种完全无锁的内存分配器。它只使用广泛可用的操作系统支持和硬件原子指令。即使在任意线程下,它也提供了有保证的可用性 终止和崩溃失败,并且无论调度策略如何,它都不受死锁的影响,因此可以 甚至在中断处理程序和实时应用程序中使用 无需特殊的调度程序支持。另外,通过利用囤积中的一些高级结构,我们的分配器 具有高度的可扩展性,将空间放大限制为一个常数, 并且能够避免错误共享


    您可以使用一个无锁列表和两个大小不同的存储桶

    因此:

    typedef结构
    {
    联合{
    滑道入口;
    作废*清单;
    };
    字节mem[];
    }mem_区块;
    类型定义结构
    {
    SLIST_头根;
    }内存块列表;
    #定义BUCKET\u计数4
    #定义块\u以\u分配16
    静态内存块列表存储桶[存储桶计数];
    void init_bucket()
    {
    对于(int i=0;i根目录和块->条目;
    }
    
    SLIST\u ENTRY、interlockedpusentryslist、InterlockedPopEntrySList、InitializeSListHead
    是Win32下无锁单链表操作的函数。请在其他操作系统上使用相应的操作

    缺点:

    • sizeof(SLIST\u条目)的开销
    • 存储桶在开始时只能有效增长一次,之后可能会耗尽内存,必须询问操作系统/其他存储桶。(其他存储桶会导致碎片化)
    • 此示例有点太简单,必须扩展以处理更多的情况

    我想用C写。无论如何,谢谢。这篇论文是我看到这个问题时想到的第一件事。我们在我们的一个产品中使用了这个分配器的一个变体,它真的非常有用。谢谢Dan。这听起来很棒!所以我有信心改进它。请注意,在发表的论文中有错误。至少,算法rithm应该进行模型检查。我希望这是从2004年开始进行的,但我不知道。@Norman Ramsey:+1你完全正确……该论文应该作为一个起点。+1非常有趣的链接。你有他们在论文中使用的基准的链接吗?请注意,每线程池在生产者-消费者模型中完全失败。
    typedef struct
    {
        union{
            SLIST_ENTRY entry;
        void* list;
    };
    byte mem[];
    } mem_block;
    
    typedef struct
    {
        SLIST_HEADER root;
    } mem_block_list;
    
    #define BUCKET_COUNT 4
    #define BLOCKS_TO_ALLOCATE 16
    
    static mem_block_list Buckets[BUCKET_COUNT];
    
    void init_buckets()
    {
        for( int i = 0; i < BUCKET_COUNT; ++i )
        {
            InitializeSListHead( &Buckets[i].root );
            for( int j = 0; j < BLOCKS_TO_ALLOCATE; ++j )
            {
                mem_block* p = (mem_block*) malloc( sizeof( mem_block ) + (0x1 << BUCKET_COUNT) * 0x8 );
                InterlockedPushEntrySList( &Buckets[i].root, &p->entry );
            }
        }
    }
    
    void* balloc( size_t size )
    {
        for( int i = 0; i < BUCKET_COUNT; ++i )
        {
            if( size <= (0x1 << i) * 0x8 )
            {
                mem_block* p = (mem_block*) InterlockedPopEntrySList( &Buckets[i].root );
                p->list = &Buckets[i];
            }
        }
    
        return 0;   // block to large
    }
    
    void  bfree( void* p )
    {
        mem_block* block = (mem_block*) (((byte*)p) - sizeof( block->entry ));
        InterlockedPushEntrySList( ((mem_block_list*)block)->root, &block->entry );
    }