【内存池系列】提高C++性能的编程技术 学习笔记(二) 内存池

内存池

内存池(Memory Pool)是一种内存分配方式。 通常我们习惯直接使用new、malloc等API申请分配内存,这样做的缺点在于:由于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到提升。


以下是两种不同结构的内存池实现方法。


链式结构:

通过模板实现,使得多个类可以使用,无需重写。创建MemoryPool对象时,为模板的类对象获得大小为EXPANSION_SIZE(32)块的内存,每块的字节数为sizeof(T)。当需要使用时,调用alloc函数,将next指向的空内存块分配,释放对象时,调用free函数将内存块还给链表,添加到空闲链表头。


#ifndef _MEM_POOL_
#define  _MEM_POOL_

template<class T>
class MemoryPool
{
public: 
	MemoryPool(size_t size = EXPANSION_SIZE);
	~MemoryPool();
	inline void* alloc(size_t size);
	inline void free(void *someElement);
private:
	MemoryPool<T>* next;
	enum{EXPANSION_SIZE =32};
	void expandTheFreeList(int howMany=EXPANSION_SIZE);
};

template<class T>
MemoryPool<T>::MemoryPool(size_t size /* = EXPANSION_SIZE */)
{
	expandTheFreeList(size);
}

template<class T>
MemoryPool<T>::~MemoryPool()
{
	MemoryPool<T> *nextPtr=next;
	for(nextPtr=next;nextPtr!=NULL;nextPtr=next)
		next=next->next;
	delete [] nextPtr;
}

template<class T>
inline void* MemoryPool<T>::alloc(size_t size)
{
	if(!next)
		expandTheFreeList();
	MemoryPool<T> *head = next;
	next=head->next;
	return head;
}

template <class T>
inline void MemoryPool<T>::free(void *doomed)
{
	MemoryPool<T> *head=(MemoryPool<T>*)doomed;
	head->next=next;
	next=head;
}

template<class T>
void MemoryPool<T>::expandTheFreeList(int howMany/* =EXPANSION_SIZE */)
{
	size_t size=(sizeof(T)>sizeof(MemoryPool<T>*))? sizeof(T):sizeof(MemoryPool<T>*);
	MemoryPool<T>* runner=(MemoryPool<T>*)new char[size];
	next=runner;
	for(int i=0;i<howMany;i++){
		runner->next=(MemoryPool<T>*)new char[size];
		runner=runner->next;
	}
	runner->next=0;
}
#endif

可变大小的内存管理器:


#ifndef _MEM_CHUNK_
#define _MEM_CHUNK_
class MemoryChunk
{
private:
	MemoryChunk *next;
	void *mem;
	size_t chunkSize; //一个内存块的默认大小
	size_t bytesAlreadyAllocated; //当前内存块中已分配的字节数
public:
	MemoryChunk(MemoryChunk *nextChunk,size_t chunkSize);
	~MemoryChunk();
	inline void *alloc(size_t size);
	inline void free(void* someElement);
	
	//指向列表下一内存块的指针
	MemoryChunk *nextMemChunk() {return next;}
	//当前内存块剩余空间大小
	size_t spaceAvailable() {return chunkSize-bytesAlreadyAllocated;}

	enum{DEFAULT_CHUNK_SIZE=4096};
};
inline void* MemoryChunk::alloc(size_t requestSize)
{
	void* addr=(void*)((size_t)mem+bytesAlreadyAllocated);
	bytesAlreadyAllocated+=requestSize;
	return addr;
}

inline void MemoryChunk::free(void* doomed) {}
#endif

#include "stdafx.h"
#include "MemoryChunk.h"

MemoryChunk::MemoryChunk(MemoryChunk *nextChunk,size_t reqSize)
{
	chunkSize=(reqSize>DEFAULT_CHUNK_SIZE)? reqSize:DEFAULT_CHUNK_SIZE;
	next=nextChunk;
	bytesAlreadyAllocated=0;
	mem=new char[chunkSize];
}

MemoryChunk::~MemoryChunk()
{
	delete [] mem;
}

#ifndef _BYTE_MEM_POOL_
#define _BYTE_MEM_POOL_
#include "MemoryChunk.h"
class ByteMemoryPool
{
private:
	//内存块列表
	MemoryChunk *listofMemoryChunks;
	//向内存块列表添加一个内存块
	void expandStorage(size_t reqSize);
public:
	ByteMemoryPool(size_t initSize=MemoryChunk::DEFAULT_CHUNK_SIZE);
	~ByteMemoryPool();

	//从私有内存池分配内存
	inline void *alloc(size_t requestSize){
		size_t space=listofMemoryChunks->spaceAvailable();
		if(space<requestSize)
		{
			expandStorage(requestSize);
		}
		return listofMemoryChunks->alloc(requestSize);
		
	}
	//释放先前从内存池中分配的内存
	inline void free(void* doomed)
	{
		listofMemoryChunks->free(doomed);
	}
};

#endif

#include "stdafx.h"
#include "ByteMemoryPool.h"

ByteMemoryPool::ByteMemoryPool(size_t initSize):listofMemoryChunks(0)
{
	expandStorage(initSize);
}
ByteMemoryPool::~ByteMemoryPool()
{
	MemoryChunk *memChunk=listofMemoryChunks;
	while(memChunk){

		listofMemoryChunks=memChunk->nextMemChunk();
		delete memChunk;
	
		memChunk=listofMemoryChunks;

	}
}

void ByteMemoryPool::expandStorage(size_t reqSize)
{
	listofMemoryChunks=new MemoryChunk(listofMemoryChunks,reqSize);
}


使用内存池能够极大提高内存的使用效率和程序的执行效率。笔者在不使用内存池和使用以上两个不同版本的内存池的情况分别对程序的执行时间进行了测试;

测试代码如下:

// SimpleAlloc.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <ctime>
using namespace std;

class Rational{
private:
	int n;
	int d;
public:
	Rational(int a=0,int b=1):n(a),d(b){}
};

int _tmain(int argc, _TCHAR* argv[])
{
	size_t start,end;
	Rational *array[1000];
	start=clock();
	for(int j=0;j<500;j++){
		for(int i=0;i<1000;i++){
			array[i]=new Rational(i);
		}
		for(int i=0;i<1000;i++)
			delete array[i];
	}
	end=clock();
	cout<<end-start;
	return 0;
}

不使用内存池,直接使用new和delete管理内存的情况下,以上程序执行耗时426ms,使用版本一内存池耗时99ms,版本二内存池耗时216ms。不难看出,使用内存池能够极大提高执行效率,同时不会产生外部内存碎片。版本二内存池相较版本一内存池虽然较慢,但增加了分配逻辑的复杂性,同时更加强大。



还有一篇更好的文章,讲的非常清楚,链接:http://blog.csdn.net/shawngucas/article/details/6574863


你可能感兴趣的:(【内存池系列】提高C++性能的编程技术 学习笔记(二) 内存池)