高性能內(nèi)存池實現(xiàn)

對于從事c/c++開發(fā)的人來說,malloc/new再也熟悉不過了,對于堆上的內(nèi)存分配,都是使用標準庫提供的函數(shù)來進行內(nèi)存分配,而這些函數(shù)最終也會進入到系統(tǒng)調(diào)用(brk等),每次的內(nèi)存申請和釋放,都可能會涉及到底層內(nèi)存數(shù)據(jù)的調(diào)整,所以效率會非常低。如果我們一次申請一塊很大的內(nèi)存塊,后續(xù)所有的內(nèi)存申請和分配,都是基于這一塊內(nèi)存來進行,這樣效率就會提升很多,本文主要就是實現(xiàn)一個高效的固定大小的內(nèi)存池。
結構

? ?圖一
實現(xiàn)????
內(nèi)存塊定義:
typedef?struct?memory_block {unsigned int size;unsigned int free_size;unsigned int first_free;struct memory_block *next;char a_data[1];} s_memory_block;
內(nèi)存池Header:
typedef?struct?memory_pool {unsigned int unit_size;unsigned int init_size;unsigned int grow_size;s_memory_block *block;} s_memory_pool;
初始化內(nèi)存池:
//此函數(shù)初始化內(nèi)存池Headers_memory_pool?*memory_pool_create(unsigned?int?size) {s_memory_pool *mp;mp = (s_memory_pool*)malloc(sizeof(s_memory_pool));mp->first_block = NULL;mp->init_size = 10000;mp->grow_size = 10000;if(size < sizeof(unsigned int))mp->obj_size = sizeof(unsigned int);????????//對齊mp->unit_size = (size + (MEMPOOL_ALIGNMENT-1)) & ~(MEMPOOL_ALIGNMENT-1);return mp;}
當初始化之后,內(nèi)存池結構變成:

? ?圖二
從函數(shù)實現(xiàn)內(nèi)容來看,是初始化了內(nèi)存池的頭。
內(nèi)存分配函數(shù):
1、從mp的first_block開始,如果其為空,則表明該內(nèi)存池為首次創(chuàng)建,需要分配內(nèi)存塊,并在該內(nèi)存塊內(nèi)進行鏈式初始化,返回該塊的第一小塊地址。
2、從first_block開始,查找一個有內(nèi)存可分配的block,如果有,則分配,并將first_free指向該塊的下一個地址。
3、重新建一個block,進行分配,并將該新塊插入到mp的頭部
void?*memory_alloc(s_memory_pool?*mp) {register unsigned int i;register char *data;//unsigned int length;if(mp->first_block == NULL)//memory_pool is NULL{????????????????s_memory_block *mb;mb = (s_memory_block *)malloc((mp->init_size)*(mp->obj_size) + sizeof(s_memory_block));//create first memory_blockif(mb == NULL){perror("memory allocate failed!\n");return NULL;}/* init the first block */mb->next = NULL;mb->free_size = mp->init_size - 1;mb->first_free = 1;mb->size = mp->init_size*mp->obj_size;mp->first_block = mb;data = mb->a_data;/* set the mark */for(i=1; iinit_size; ++i) {???????????????????????//初始化塊,鏈接方式類似于鏈表}return (void *)mb->a_data;????????}?//如圖三s_memory_block *pm_block = mp->first_block;while((pm_block!=NULL) && (pm_block->free_size==0)){pm_block = pm_block->next;}if(pm_block != NULL){char *pfree = pm_block->a_data + pm_block->first_free * mp->obj_size;????????????????//查找一個可用塊返回????????????????// ......return (void *)pfree;}else{if(mp->grow_size == 0)return NULL;s_memory_block *new_block = (s_memory_block *)malloc((mp->grow_size)*(mp->obj_size) + sizeof(s_memory_block));if(new_block == NULL)return NULL;data = new_block->a_data;for(i=1; igrow_size; ++i) {????????????????????????//鏈式}???????????????//將新塊插入到鏈表頭首部return (void *)(new_block->a_data);}}
釋放函數(shù):
1、遍歷該內(nèi)存池,查找所要釋放的內(nèi)存塊pfree所在的block
2、將該block的first指向該pfree的偏移
3、該pfree的偏移指向之前block的first
注:2、3處相當于鏈表的插入
inline void* memory_free(s_memory_pool *mp, void *pfree){if(mp->first_block == NULL)return;s_memory_block *pm_block = mp->first_block;s_memory_block *pm_pre_block = mp->first_block;/* research the memory_block which the pfree in */while(pm_block && ((unsigned int)pfree<(unsigned int)pm_block->a_data ||(unsigned int)pfree>((unsigned int)pm_block->a_data+pm_block->size))){//pm_pre_block = pm_block;pm_block = pm_block->next;if(pm_block == NULL)return NULL;}??//獲取偏移地址unsigned int offset = pfree -(void*) pm_block->a_data;if((offset&(mp->obj_size -1)) > 0)return pfree;??//將釋放的內(nèi)存塊返回給內(nèi)存池對應的Block塊pm_block->free_size++;*((unsigned int *)pfree) = pm_block->first_free;pm_block->first_free=(unsigned int)(offset/mp->obj_size);??return?NULL;}

圖三
當內(nèi)存池為空的時候,也就是首次調(diào)用memory_alloc函數(shù)時候,會創(chuàng)建一個新的Block,并對這個Block進行初始化,然后返回首塊地址?。
當?shù)诙握{(diào)用memory_alloc之后,如圖四所示。
當?shù)谌握{(diào)用memory_alloc之后,如圖五所示。? ?????????????????????????????????????????

圖四

圖五

圖六
當釋放第二次分配的內(nèi)存之后,整個內(nèi)存塊鏈表如圖六所示。

圖八
當現(xiàn)在所有的Block里面都沒有可用內(nèi)存之后,就重新申請一塊Block,并插入到Header的頭部,如圖八所示。
內(nèi)存池數(shù)據(jù)結果:
與庫函數(shù)malloc相比,性能提升了大概25%左右
注:本文旨在于提供一種設計思路,在本文實現(xiàn)的內(nèi)存池,僅僅支持單線程,固定大小的,讀者可以針對該思路,進行改進
