摘自:http://blog.sina.com.cn/s/blog_54384df801019ahp.html
以前自己写过一个内存池,采取FreeList计数,总感觉那个性能无与伦比。
但上次看一个人测试基于boost::object_pool,和CRT的new/delete的速度比较。
在10000*10000次的循环中,简单的对三个大小不一样的class做了new/delete的操作,并简单做些加减。
然后那人说1亿次的操作,boost::object_pool是3秒,传统的是93秒。
本以为自己实现的虽不如boost,怎么也得十几秒吧?然后在自己机器上测了一下,果然,boost的是3秒。我自己以前写的那个内存池惨不忍睹。运行好久没结果。
于是痛定思痛,发现自己实现的内存池太想搞个大而全的内存管理,从而性能极度垃圾。
基于boost::object_pool,自己实现了一个简单而性能卓越的对象池。
以下是设计思路:
首先,实现一个模板化的对象池。而且尽量简洁,本来我还想在里头弄些map或hash_map,用来标记已经使用的对象,不过这个性能极度令人不满意,于是放弃。
这第一次实现的非常简单,只是使用FreeList技术分配对象,性能与boost::object_pool一个级别,而且非常简答。
// 锁
struct ZgmLock
{
ZgmLock(::CRITICAL_SECTION*cs):pcs(cs) { ::EnterCriticalSection(pcs); }
~ZgmLock() { ::LeaveCriticalSection(pcs); }
::CRITICAL_SECTION*pcs;
};
template
class ZgmObjectPool
{
public:
typedef T ObjectType;
private:
typedef std::map FreePointer;
typedef std::vector FreeIndex;
FreePointer m_FreePointerIndexs;// 空闲指针索引map,key为growSize
FreeIndex m_FreeIndexs; // 空闲索引列表
int m_growSize; // 内存增长的大小
::CRITICAL_SECTION m_cs;
void Grow()
{
int objectSize = sizeof(ObjectType);
char* pData = static_cast< char * >( ::malloc( m_growSize * objectSize) );
if(pData == NULL) return;
// 加入指针map中
m_FreePointerIndexs.insert(std::make_pair(m_growSize,pData));
// 加入空闲索引中
for(int i = 0;i < m_growSize;++i)
m_FreeIndexs.push_back(pData + i);
// 下次增长一倍
m_growSize *= 2;
}
char* GetFreePointer()
{
if(m_FreeIndexs.empty())
Grow();
if(m_FreeIndexs.empty())
return NULL;
char*pData = m_FreeIndexs.back();
m_FreeIndexs.pop_back();
return pData;
}
public:
ZgmObjectPool() : m_growSize(32)
{
::InitializeCriticalSection(&m_cs);
}
~ZgmObjectPool()
{
::DeleteCriticalSection(&m_cs);
}
// 构造一个对象[默认构造函数]
ObjectType* construct()
{
ZgmLock lock(&m_cs);
char*pData = GetFreePointer();
if(pData == NULL) return NULL;
ObjectType * const ret = new (pData) ObjectType();
return ret;
}
// 销毁一个对象
void destroy(ObjectType * const object)
{
ZgmLock lock(&m_cs);
object->~T();
char*pData = (char*)(object);
m_FreeIndexs.push_back(pData);
}
};
以上代码非常简单。性能确实很好,但内存一旦增加上去,无法降下来。
本来是想用个map或者hash_map将用过的收集起来,destroy的时候去map中查询,一旦发现可以回收内存就回收一块内存,于是发现一亿次的分配销毁操作,本来在3秒就可以结束的,直接变成无法接受了。
于是想到java的GC功能,实现了一个gc操作【这个gc操作不要太频繁】。
// 内存回收
void gc(bool bEnforce = false)
{
ZgmLock lock(&m_cs);
ObjectType * object;
char*pData;
// 构造一个map来使查找m_FreeIndexs更加快捷一些
std::map findexs;
{
typename FreeIndex::iterator fit = m_FreeIndexs.begin(),fitEnd = m_FreeIndexs.end();
for(;fit != fitEnd;++fit)
{
findexs.insert(std::make_pair(*fit,true));
}
}
std::vector deleteList;
bool bCanGC;
int growSize;
//回收内存
typename FreePointer::iterator it = m_FreePointerIndexs.begin(),itEnd = m_FreePointerIndexs.end();
for(;it != itEnd;++it)
{
// 查找是否可以回收[即自己的指针是否全部在m_FreeIndexs,有一个不在则已分配至少一份出去,不可回收]
bCanGC = true;
for(int i = 0;i < it->first;++i)
{
pData = it->second + i;
if(findexs.find(pData) == findexs.end())
{
if(!bEnforce)
{
bCanGC = false;
break;
}
else
{
// 强制回收则都要回收掉
object = (ObjectType *)pData;
object->~T();
}
}
}
// 可以回收
if(bCanGC)
{
// 回收空闲索引
for(int i = 0;i < it->first;++i)
{
pData = it->second + i;
findexs.erase(pData);
}
// 回收指针
::free( static_cast< void * >( it->second ) );
// 删除该key
deleteList.push_back(growSize);
// 下次减少一倍
m_growSize /= 2;
}
}
// 写回空闲索引
m_FreeIndexs.clear();
typename std::map::iterator fit = findexs.begin(),fitEnd = findexs.end();
for(;fit != fitEnd;++fit)
{
m_FreeIndexs.push_back(fit->first);
}
// 真正删除
for(int i = 0;i < (int)deleteList.size();++i)
{
m_FreePointerIndexs.erase(deleteList[i]);
}
}
必要时使用上头的gc函数回收内存。这样,性价比就好很多了。只在必要的时候调用一下gc(false),即可回收内存。
该gc函数比较牛逼的是,只要再析构函数中调用gc(true),那么对象池会帮你将没有调用destroy的对象调用之。也就是说,这个对象池如今与java的垃圾收集器已经极为接近,性能也无与伦比。
写到这里,可能一般人认为就该结束了,这与boost:object_pool大同小异,性能也差不多,也实现差不多的垃圾回收机制。
但我要说,不,还没完呢,这只是设计到一半,真正的内存管理才刚刚开始。
我们需要组织任何对象,也就是说N组对象池,实现对整个系统的对象的管理,并从而管理内存。
于是我们需要一个对象池工厂。
看以下代码实现。
// 对象池指针定义
#define DefineObjectPoolPtr(T,pPool) ZgmObjectPool* pPool
// 获得特定对象池指针。
#define GetObjectPoolPtr(T) ZgmObjectFactory::GetSingleton().GetObjectPool(ZgmType2Type(),#T)
// 直接定义对象池
#define ObjectPoolPtr(T,pPool) DefineObjectPoolPtr(T,pPool) = GetObjectPoolPtr(T)
//
// 对象池
template class ZgmObjectPool;
{
::InitializeCriticalSection(&m_cs);
}
::CRITICAL_SECTION m_cs;
typedef std::map PoolMap;
PoolMap m_poolMap;
public:
~ZgmObjectFactory()
{
::DeleteCriticalSection(&m_cs);
}
// 获得单例
static ZgmObjectFactory& GetSingleton()
{
static ZgmObjectFactory poolFactory;
return poolFactory;
}
// 获得ObjectPool
template
ZgmObjectPool* GetObjectPool(const ZgmType2Type& t2t, const std::string&sname)
{
ZgmLock lock(&m_cs);
ZgmObjectPool*pool;
PoolMap::iterator it = m_poolMap.find(sname);
if(it == m_poolMap.end())
{
pool = new ZgmObjectPool();
m_poolMap.insert(std::make_pair(sname,pool));
}
else
{
pool = (ZgmObjectPool*)it->second;
}
return pool;
}
// 全体gc
void gc()
{
ZgmLock lock(&m_cs);
PoolMap::iterator it = m_poolMap.begin(),itEnd = m_poolMap.end();
for(;it != itEnd;++it)
it->second->gc(false);
}
};
好了,有了上面的对象池工厂。
我们让我们的ZgmObjectPool模板类public 继承自ZgmObjectPoolInterface,于是一个牛逼的系统实现了。
我们仅需使用以上定义的宏,即可在整个系统中管理所有对象。
在任意一处代码中要对某个对象使用对象池技术,那么仅需如此:
struct A
{
int data[10];
};
struct B
{
short data[10];
};
ObjectPoolPtr(A,pAPool);
ObjectPoolPtr(B,pBPool);
for(int j = 0;i < 10000;i++)
{
A*pa = pAPool->constrcut();
B*pb = pBPool->constrcut();
// to do something
pa->data[0] += pb->data[0];
pAPool->destroy(pa);
pBPool->destroy(pb);
}
以上就是代码示例。
于是散乱的对象池被我们的对象池工厂完美的组织成了一个整体。
这是多么完美的一次设计!!!
完整代码如下【更加有效的,支持多构造函数的对象池工厂和对象池】:
#pragma once
#include
#include
#include
#include
#include