一个简单的内存池(c实现)之一
|
都知道频繁分配内存释放内存很耗系统资源,而且容易造成内存碎片。因此写了个简单的内存池实现,越简单越好,为什么?做复杂了效率还不如直接malloc。因此这个内存池采用链表连接内存块的方式,分配的是固定大小的内存块,从池中取内存和归还内存是用的空闲链表堆栈操作, 没有使用线程锁,如果要线程安全,建议在外部调用内存池的地方加锁。
做过一个简单的测试,10万次内存池调用的效率大概比直接分配释放内存提高了30-50%。但是前提是内存池不能加锁(pthread_mutex),加锁的内存池效率和直接分配内存的效率差不多,有时候还要多点点。(测试的环境是每次2K,4个双核CPU,FREEBSD7) 代码实现: struct memblock { int used; void* data; struct memblock* next; struct memblock* createnext; };
struct mempool
上面这个内存池的实现其实更像一个后备列表的实现。使用上来说不是很方便,要申请的内存块是一个BLOCK结构的一个个成员,而且每次从系统内存堆中申请都是一小块一小块,也没有考虑字节对齐。因此让我们来看看新的一个内存池的实现吧。 这个内存池是根据《c++应用程序性能优化》书里的固定尺寸的内存池原理做了一些改动用C语言写的。大家有兴趣可以去看看,里面说的最详细。 简单说下这个内存池的原理,内存池里由N个memblock以一个双向链表组成,每个memblock的组成是一个HEAD块+M个固定长度的memchunk组成,memchunk就是你将来要从池中申请的内存块。
我们来看下如下几个情况: 1.内存池初始化后,内存池的memblock链表头是NULL。 2.第一次从池中申请一个memchunk,内存池根据initsize和chunksize从系统内存堆中申请一个(memblock head)+ chunksize*initsize的内存块,对block head部分数据字段进行初始化,并将每个chunk的头4个字节来存放该memblock里下个可用chunk的编号,因为是固定长度的chunk,所以,可以很容易根据编号和chunk长度计算出chunk的地址。创建了memblock后,将第一个chunk
(设为A) 返回给用户,并将block head的first字段设置为chunk A头4个字节的值(也就是下个可用chunk编号)。同时将创建的block加入到链表头中。 3.下次申请memchunk的时候,遍历链表,找出有空闲chunk的BLOCK,对BLOCK进行和第一次申请时类似的处理。同时检查该BLOCK里还有多余的空闲chunk不,有的话就将该block移动到链表头部。以提高下次申请时遍历链表的速度。如果遍历完链表也没有找到有空闲chunk的block,就从系统内存堆中申请一个BLOCK,将之加入到链表头。
4.将申请的memchunk (假设为A)归还给池的时候,遍历memblock链表,根据A的地址来找出A所在的block。 找到后根据这个 memchunk A 的地址计算出它的编号; 将block->first 的编号存入A的头4个字节中; 将block->first更改为A的编号。(就是chunk的链表操作) 最后,将A所在的这个memblock移动到链表头(因为有空闲chunk),以提高申请chunk时的速度。(链表只需遍历一次)。在书中,这里还有个处理:如果该block的chunk都是空闲的,就把block释放了(归还给系统内存堆),我没有这样做,打算单独写个清理的操作。
大概原理就是这样,考虑到和64位机兼容,chunk和block都按8字节对齐。代码中的memheap就是mempool。只是名称我该成heap了。。 在后面的代码中,对内存池实现有比较详细的注释。回顾下这个内存池的原理,明显的优点是减少了内存碎片,字节对齐,但是有个显而易见的问题是,如果内存池中有大量(成千上万)个memblock的话,对block的遍历检索将是一个性能瓶颈,申请chunk的操作还好点,内部做了一些优化处理,归还chunk时查找链表的速度将比较慢,最坏的情况是有多少个memblock就检索多少次。。可以考虑对这里做一些检索上的优化和更改,不用双向链表,用其他方式来做。最简单的优化就是用游戏粒子系统里普遍使用的一种算法,将有空闲chunk的block放一个链表,没有空闲chunk的block放另外一个链表,再做一些分配上的改动,也许能提高一些速度。{ int size;//memblock大小 int unused;//空闲的memblock大小 int datasize;//每次分配的数据大小(就是memblock.data) struct memblock* free_linkhead;//空闲memblock链表头 struct memblock* create_linkhead;//所有创建的memblock链表头,内存池释放的时候使用,防止内存池释放的似乎还有memblock未归还的情况 }; typedef void (*free_callback)(void*);//释放回调函数,释放membloc.data用,可以简单的直接用free函数 void mempool_init(int initialSize,int datasize);//初始化mempool void mempool_dealloc(struct mempool* pool,free_callback callback);//释放mempool void* mempool_get(struct mempool* pool);//获取一个memblock void mempool_release(struct mempool* pool,struct memblock* block);//归还一个memblock /********************************* * mempool * ******************************/ //malloc一个memblock static struct memblock* mempool_allocblock( struct mempool* pool ); //------------------implement-------- void* mempool_init( int initialSize, int datasize ) { struct mempool* pool = malloc( sizeof( struct mempool ) ); pool->unused = 0; pool->datasize = datasize; pool->free_linkhead = NULL; //预先初始化initialSize个内存块 pool->create_linkhead = NULL; int i; for ( i = 0; i < initialSize; i++ ) { struct memblock* block = mempool_allocblock( pool ); mempool_release( pool, block ); } return ( pool ); } void mempool_dealloc( struct mempool* pool, free_callback callback ) { struct memblock* block = NULL; //将所有创建的memblock释放了 while ( pool->create_linkhead != NULL ) { block = pool->create_linkhead; pool->create_linkhead = pool->create_linkhead->createnext; //执行free回调。 if ( callback ) { ( *callback )( block->data ); } free( block ); } free( pool ); L_DEBUG( "%s:size(%d),unused(%d)", __func__, pool->size, pool->unused ); } static struct memblock* mempool_allocblock( struct mempool* pool ) { struct memblock* block = malloc( sizeof( struct memblock ) ); block->data = malloc( sizeof( pool->datasize ) ); block->next = NULL; block->used = 1;//表示已使用 //加入所有创建的memblock的链表头 block->createnext = pool->create_linkhead; pool->create_linkhead = block; pool->size++; return ( block ); } void mempool_release( struct mempool* pool, struct memblock* block ) { if ( block == NULL ) { L_WARN( "%s:release a NULL!", __func__ ); return; } if ( block->used != 1 ) { L_WARN( "%s:used!=1", __func__ ); return; } //将归还的内存块放到空闲链表头。 block->used = 0;//表示空闲 block->next = pool->free_linkhead; pool->free_linkhead = block; pool->unused++;//空闲数+1 } void* mempool_get( struct mempool* pool ) { struct memblock* block = NULL; if ( pool->free_linkhead ) { //从空闲链表头取出一个内存块 block = pool->free_linkhead; pool->free_linkhead = pool->free_linkhead->next; block->next = NULL; block->used = 1;//表示已使用 pool->unused--;//空闲内存块数-1 } else { //没有空闲的内存块,创建一个 block = mempool_allocblock( pool ); } return ( block ); } |


}

被折叠的 条评论
为什么被折叠?



