<返回更多

C++内存池实现

2019-08-28    
加入收藏

内存池是自己向OS请求的一大块内存,自己进行管理。

##系统调用## 我们先测试系统调用new/delete的用时。

#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;
}

用时为604124400ns。系统的new是在堆上分配资源,每次执行都会分配然后销毁。

##简单的内存池##

#include <iostream>
#include <time.h> 
using namespace std;
char buf[4100]; //已分配内存
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;
}

用时为39420791ns,后者比前者快20倍。

简单内存池在开始在全局/静态存储区分配资源,一直存在。每次重载的new调用只是返回了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; //一个单元的内存大小
	unsigned long m_ulBlockSize; //整个内存池的内存大小
public:
	CMemPool(unsigned long lUnitNum = 50, unsigned long lUnitSize = 1024);
	~CMemPool();
	void* Alloc(unsigned long ulSize, bool bUseMemPool = true);
	void Free(void* p);
};

CMemPool定义了一个_Unit来管理链表,指针被包含在整个结构中,这种方式和内核中的链表写法很像。

m_pMemBlock指向分配的那块大小为m_ulBlockSize的大内存的地址。m_pMemBlock是线性的内存,我们把它用下列这种方式管理。

C++内存池实现

 

它被均分为lUnitNum个大小为m_ulUnitSize Byte的小内存块。每个块分为2部分:Unit链表管理头真正进行存储的内存单元

从图中可以看出m_ulBlockSize的计算方式为:

UnitNum * ( UnitSize + sizeof(Struct _Unit))

然后用双向链表连接所有的小块。m_pFreeMemBlock指向空闲的内存的起始位置,m_pAllocatedMemBlock指向已分配出去的内存的起始位置。

##MemPool实现##

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;
		}
	}
}

构造函数设置默认的小块数为50,每个小快大小为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的作用是分配内存,返回分配的内存地址,注意加上Unit的大小是为了略过Unit管理头。实质是把m_pFreeMemBlock指向的free内存移动到m_pAllocatedMemBlock指向的已分配内存里。

每次分配时,m_pFreeMemBlock指针后移。pCurUnit从前面插入到m_pAllocatedMemBlock里。

void CMemPool::Free(void* p)
{
	if(m_pMemBlock<p && p<(void*)((char*)m_pMemBlock + m_ulBlockSize))
	{
		//判断释放的内存是不是处于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的作用是释放内存,实质是把m_pAllocatedMemBlock指向的已分配内存移动到m_pFreeMemBlock指向的free内存里。和Alloc的作用相反。

pCurUnit要减去struct _Unit是为了从存储单元得到管理头的位置,堆是向上生长的。

##测试##

#include "mempool.h"
#include <time.h> 
CMemPool g_MemPool;
class CTestClass
{
public:
	void *operator new(size_t);	 //重载运算符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的内存给它
}
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;
		//使用内存池测试
		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;
		
		//使用系统调用测试
		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;
}

##结果##

从下图可以看出,只有当程序频繁地用系统调用malloc/free或者new/delete分配内存时,内存池有价值。

C++内存池实现

 

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>