摘自: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