內(nèi)存池是自己向OS請求的一大塊內(nèi)存,自己進(jìn)行管理。
##系統(tǒng)調(diào)用## 我們先測試系統(tǒng)調(diào)用new/delete的用時(shí)。
#include <IOStream>
#include <time.h>
using namespace std;
class TestClass
{
private:
char m_chBuf[4096];
};
timespec diff(timespec start, timespec end)
{
timespec temp;
temp.tv_sec = end.tv_sec-start.tv_sec;
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
return temp;
}
int main()
{
timespec time1, time2;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1);
for(unsigned int i=0; i< 0x5fffff; i++)
{
TestClass *p = new TestClass;
delete p;
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2);
cout<<diff(time1,time2).tv_sec<<":"<<diff(time1,time2).tv_nsec<<endl;
}
用時(shí)為604124400ns。系統(tǒng)的new是在堆上分配資源,每次執(zhí)行都會分配然后銷毀。
##簡單的內(nèi)存池##
#include <iostream>
#include <time.h>
using namespace std;
char buf[4100]; //已分配內(nèi)存
class TestClass
{
public:
void* operator new(size_t)
{return (void*)buf;}
void operator delete(void* p){}
private:
char m_chBuf[4096];
};
int main()
{
timespec time1, time2;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1);
for(unsigned int i=0; i< 0x5fffff; i++)
{
TestClass *p = new TestClass;
delete p;
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2);
cout<< diff(time1,time2).tv_sec<<":"<<diff(time1,time2).tv_nsec<< endl;
}
用時(shí)為39420791ns,后者比前者快20倍。
簡單內(nèi)存池在開始在全局/靜態(tài)存儲區(qū)分配資源,一直存在。每次重載的new調(diào)用只是返回了buf的地址,所以快。
##MemPool定義##
class CMemPool
{
private:
struct _Unit
{
struct _Unit *pPrev, *pNext;
};
void* m_pMemBlock;
struct _Unit* m_pFreeMemBlock;
struct _Unit* m_pAllocatedMemBlock;
unsigned long m_ulUnitSize; //一個(gè)單元的內(nèi)存大小
unsigned long m_ulBlockSize; //整個(gè)內(nèi)存池的內(nèi)存大小
public:
CMemPool(unsigned long lUnitNum = 50, unsigned long lUnitSize = 1024);
~CMemPool();
void* Alloc(unsigned long ulSize, bool bUseMemPool = true);
void Free(void* p);
};
CMemPool定義了一個(gè)_Unit來管理鏈表,指針被包含在整個(gè)結(jié)構(gòu)中,這種方式和內(nèi)核中的鏈表寫法很像。
m_pMemBlock指向分配的那塊大小為m_ulBlockSize的大內(nèi)存的地址。m_pMemBlock是線性的內(nèi)存,我們把它用下列這種方式管理。
它被均分為lUnitNum個(gè)大小為m_ulUnitSize Byte的小內(nèi)存塊。每個(gè)塊分為2部分:Unit鏈表管理頭,真正進(jìn)行存儲的內(nèi)存單元。
從圖中可以看出m_ulBlockSize的計(jì)算方式為:
UnitNum * ( UnitSize + sizeof(Struct _Unit))
然后用雙向鏈表連接所有的小塊。m_pFreeMemBlock指向空閑的內(nèi)存的起始位置,m_pAllocatedMemBlock指向已分配出去的內(nèi)存的起始位置。
##MemPool實(shí)現(xiàn)##
CMemPool::CMemPool(unsigned long ulUnitNum, unsigned long ulUnitSize):
m_pMemBlock(NULL), m_pAllocatedMemBlock(NULL), m_pFreeMemBlock(NULL),
m_ulBlockSize(ulUnitNum * (ulUnitSize+sizeof(struct _Unit))),
m_ulUnitSize(ulUnitSize)
{
m_pMemBlock = malloc(m_ulBlockSize);
if(NULL != m_pMemBlock)
{
for(unsigned long i = 0; i<ulUnitNum; i++)
{
struct _Unit* pCurUnit=(struct _Unit*)((char*)m_pMemBlock
+ i*(ulUnitSize+sizeof(struct _Unit)) );
pCurUnit->pPrev = NULL;
pCurUnit->pNext = m_pFreeMemBlock;
if(NULL != m_pFreeMemBlock)
{
m_pFreeMemBlock->pPrev = pCurUnit;
}
m_pFreeMemBlock = pCurUnit;
}
}
}
構(gòu)造函數(shù)設(shè)置默認(rèn)的小塊數(shù)為50,每個(gè)小快大小為1024,最后用雙向鏈表管理它們,m_pFreeMemBlock指向開始。
void* CMemPool::Alloc(unsigned long ulSize, bool bUseMemPool)
{
if(ulSize > m_ulUnitSize || false == bUseMemPool ||
NULL == m_pMemBlock || NULL == m_pFreeMemBlock)
{
cout << "System Call" << endl;
return malloc(ulSize);
}
struct _Unit *pCurUnit = m_pFreeMemBlock;
m_pFreeMemBlock = pCurUnit->pNext;
if(NULL != m_pFreeMemBlock)
{
m_pFreeMemBlock->pPrev = NULL;
}
pCurUnit->pNext = m_pAllocatedMemBlock;
if(NULL != m_pAllocatedMemBlock)
{
m_pAllocatedMemBlock->pPrev = pCurUnit;
}
m_pAllocatedMemBlock = pCurUnit;
cout << "Memory Pool" << endl;
return (void*)((char*)pCurUnit + sizeof(struct _Unit));
}
Alloc的作用是分配內(nèi)存,返回分配的內(nèi)存地址,注意加上Unit的大小是為了略過Unit管理頭。實(shí)質(zhì)是把m_pFreeMemBlock指向的free內(nèi)存移動到m_pAllocatedMemBlock指向的已分配內(nèi)存里。
每次分配時(shí),m_pFreeMemBlock指針后移。pCurUnit從前面插入到m_pAllocatedMemBlock里。
void CMemPool::Free(void* p)
{
if(m_pMemBlock<p && p<(void*)((char*)m_pMemBlock + m_ulBlockSize))
{
//判斷釋放的內(nèi)存是不是處于CMemPool
cout << "Memory Pool Free" << endl;
struct _Unit* pCurUnit = (struct _Unit*)((char*)p -
sizeof(struct _Unit));
m_pAllocatedMemBlock = pCurUnit->pNext;
if(NULL != m_pAllocatedMemBlock)
{
m_pAllocatedMemBlock->pPrev == NULL;
}
pCurUnit->pNext = m_pFreeMemBlock;
if(NULL != m_pFreeMemBlock)
{
m_pFreeMemBlock->pPrev = pCurUnit;
}
m_pFreeMemBlock = pCurUnit;
}
else
{
free(p);
}
}
Free的作用是釋放內(nèi)存,實(shí)質(zhì)是把m_pAllocatedMemBlock指向的已分配內(nèi)存移動到m_pFreeMemBlock指向的free內(nèi)存里。和Alloc的作用相反。
pCurUnit要減去struct _Unit是為了從存儲單元得到管理頭的位置,堆是向上生長的。
##測試##
#include "mempool.h"
#include <time.h>
CMemPool g_MemPool;
class CTestClass
{
public:
void *operator new(size_t); //重載運(yùn)算符new
void operator delete(void *p);
private:
char m_chBuf[1000];
};
void *CTestClass::operator new(size_t uiSize)
{
return g_MemPool.Alloc(uiSize); //分配g_MemPool的內(nèi)存給它
}
void CTestClass::operator delete(void *p)
{
g_MemPool.Free(p);
}
class CTestClass2
{
private:
char m_chBuf[1000];
};
timespec diff(timespec start, timespec end)
{
timespec temp;
temp.tv_sec = end.tv_sec-start.tv_sec;
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
return temp;
}
int main()
{
timespec time1, time2;
for(int iTestCnt=1; iTestCnt<=10; iTestCnt++)
{
unsigned int i;
//使用內(nèi)存池測試
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1);
for(i=0; i<100000*iTestCnt; i++)
{
CTestClass *p = new CTestClass;
delete p;
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2);
cout << "[ Repeat " << 100000*iTestCnt << " Times ]"
<< "Memory Pool Interval = " << diff(time1,time2).tv_nsec
<< "ns" << endl;
//使用系統(tǒng)調(diào)用測試
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1);
for(i=0; i<LOOP_TIMES; i++)
{
CTestClass2 *p = new CTestClass2;
delete p;
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2);
cout << "[ Repeat " << LOOP_TIMES << " Times ]"
<< "System Call Interval = " << diff(time1,time2).tv_nsec
<< "ns" << endl;
}
return 0;
}
##結(jié)果##
從下圖可以看出,只有當(dāng)程序頻繁地用系統(tǒng)調(diào)用malloc/free或者new/delete分配內(nèi)存時(shí),內(nèi)存池有價(jià)值。






