如何在C中编写线程安全且高效,无锁的内存分配器?我的意思是效率:
快速分配&解除分配
最佳内存使用量(最小浪费且无外部碎片)
最小的元数据开销
答案 0 :(得分:12)
http://www.research.ibm.com/people/m/michael/pldi-2004.pdf
本文介绍了一个完全无锁的内存分配器。它仅使用广泛可用的操作系统支持和硬件原子指令。即使在任意线程下也能保证可用性 终止和崩溃失败,无论调度策略如何,它都不受死锁的影响,因此它可以 甚至在中断处理程序和实时应用程序中使用 无需特殊的调度程序支持。此外,通过利用Hoard的一些高级结构,我们的分配器 高度可扩展,将空间爆炸限制在一个恒定的因素, 并且能够避免错误分享...
答案 1 :(得分:3)
取决于效率的含义。如果我的关注是让事情变得更快,那么我可能会给每个线程它自己独立的内存池以及一个从该池中获取内存的自定义'malloc'。当然,如果我担心的是速度,我可能会首先避免分配。
没有一个答案;你将平衡一系列担忧。获得无锁分配器几乎是不可能的,但是你可以提前或不经常进行锁定(通过为每个线程分配大型池),或者你可以使锁变得如此小而紧,以至于它们必须是正确的。 / p>
答案 2 :(得分:2)
您可以使用锁定免费列表和几个不同大小的存储桶。
所以:
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 );
}
SLIST_ENTRY, InterlockedPushEntrySList, InterlockedPopEntrySList, InitializeSListHead
是Win32下无锁单链表操作的函数。使用其他操作系统上的相应操作。
缺点:
sizeof( SLIST_ENTRY )