|
改进了重复归还检测的算法,让归还对象的速度提升到与申请对象相同的级别上,增加了归还指针的合法性检测,完善了错误处理机制,基本能达到工业强度的水平了。现和大家分享分享,有用的尽管拿去,秉着开源精神,决不收版权费,但得回声一下,必竟是我一周的心血。
/*************ObjectBuffer1.1****************/
//对象缓冲器类模版,缓冲器预先分配一定数量的对象,当用到时就从缓冲器
//请求,不用时归还缓冲器,采用页链表分配技术使缓冲器理论上无分配上限
enum ObjectBuffer_ERROR
{
ObjectBuffer_ERROR_No,
ObjectBuffer_ERROR_Memory, //内存分配错误
ObjectBuffer_ERROR_NullPointer, //无效对象指针
ObjectBuffer_ERROR_MultipleReturn, //多重归还对象
};
template <class T>
class ObjectBuffer
{
public:
ObjectBuffer();
//ulObjectPage,每页上对象的数量,此值应根据对象大小、使用情况统筹选取,以降低内存占用及提高性能。
ObjectBuffer( unsigned long ulObjectOfPage );
~ObjectBuffer();
//请求一个对象
T* RequestObject();
//归还一个对象(注意:当归后*ppObject为NULL值)。
bool ReturnObject( T** ppObject );
//获取错误代码
ObjectBuffer_ERROR GetError()const{ return m_Error; }
enum
{
ObjectOfPage_Min = 16, //每页中对象数量的最小值
};
private:
struct SPage
{
T* m_pObjects; //对象数组
T** m_ppObjectStack; //对象堆,堆中存储的是对象指针
bool* m_pbIdles; //对象的空闲标志
unsigned long m_ulTop; //堆顶,当为0时说明对象已用完
unsigned long m_ulObjectCount; //对象数量
SPage* m_pFrontPage; //前一页指针
SPage* m_pBackPage; //后一页指针
bool m_bMemoryError; //内存分配错误标志
SPage()
{
m_pObjects = NULL; //对象数组
m_ppObjectStack = NULL; //对象堆,堆中存储的是对象指针
m_pbIdles = NULL; //对象的空闲标志
m_ulTop = 0; //堆顶,当为0时说明对象已用完
m_ulObjectCount = 0; //对象数量
m_pFrontPage = NULL; //前一页指针
m_pBackPage = NULL; //后一页指针
m_bMemoryError = false; //内存分配错误标志
m_ulObjectCount = ObjectOfPage_Min;
m_pObjects = new T[ m_ulObjectCount ];
m_ppObjectStack = new T*[ m_ulObjectCount ];
m_pbIdles = new bool[ m_ulObjectCount ];
if( NULL == m_pObjects || NULL == m_ppObjectStack || NULL == m_pbIdles )
{
Release();
m_bMemoryError = true;
return;
}
for( unsigned long i = 0; i < m_ulObjectCount; ++i )
{
m_ppObjectStack[m_ulObjectCount - i - 1] = &m_pObjects;
m_pIdles = true;
}
m_ulTop = m_ulObjectCount;
}
SPage( unsigned long ulObjectOfPage )
{
m_pObjects = NULL; //对象数组
m_ppObjectStack = NULL; //对象堆,堆中存储的是对象指针
m_pbIdles = NULL; //对象的空闲标志
m_ulTop = 0; //堆顶,当为0时说明对象已用完
m_ulObjectCount = ulObjectOfPage; //对象数量
m_pFrontPage = NULL; //前一页指针
m_pBackPage = NULL; //后一页指针
m_bMemoryError = false; //内存分配错误标志
if( m_ulObjectCount < ObjectOfPage_Min )m_ulObjectCount = ObjectOfPage_Min;
m_pObjects = new T[ m_ulObjectCount ];
m_ppObjectStack = new T*[ m_ulObjectCount ];
m_pbIdles = new bool[ m_ulObjectCount ];
if( NULL == m_pObjects || NULL == m_ppObjectStack || NULL == m_pbIdles )
{
Release();
m_bMemoryError = true;
return;
}
for( unsigned long i = 0; i < m_ulObjectCount; ++i )
{
m_ppObjectStack[m_ulObjectCount - i - 1] = &m_pObjects;
m_pbIdles = true;
}
m_ulTop = ulObjectOfPage;
}
~SPage(){ Release(); }
void Release()
{
if( NULL != m_pObjects )delete[] m_pObjects;
if( NULL != m_ppObjectStack )delete[] m_ppObjectStack;
if( NULL != m_pbIdles )delete[] m_pbIdles;
m_pObjects = NULL; //对象数组
m_ppObjectStack = NULL; //对象堆,堆中存储的是对象指针
m_pbIdles = NULL; //对象的空闲标志
m_ulTop = 0; //堆顶,当为0时说明对象已用完
m_ulObjectCount = 0; //对象数量
m_pFrontPage = NULL; //前一页指针
m_pBackPage = NULL; //后一页指针
m_bMemoryError = false; //内存分配错误标志
}
bool IsEmpty(){ return 0 == m_ulTop; }
bool IsFull(){ return m_ulTop == m_ulObjectCount; }
bool IsMember( T* pObject ){ return ( ( pObject >= m_pObjects ) && ( pObject <= &m_pObjects[m_ulObjectCount-1] ) ); }
T* RequestObject()
{
--m_ulTop;
m_pbIdles[ m_ppObjectStack[ m_ulTop ] - m_pObjects ] = false;
return m_ppObjectStack[ m_ulTop ];
}
ObjectBuffer_ERROR ReturnObject( T* pObject )
{
if( ( ( ( int )pObject - ( int )m_pObjects ) % sizeof( T ) ) != 0 )
{
return ObjectBuffer_ERROR_NullPointer;
}
if( IsFull() || m_pbIdles[ pObject - m_pObjects ] )
{
return ObjectBuffer_ERROR_MultipleReturn;
}
m_pbIdles[ pObject - m_pObjects ] = true;
m_ppObjectStack[ m_ulTop ] = pObject;
++m_ulTop;
return ObjectBuffer_ERROR_No;
}
};
SPage* m_pBeginPage; //开始页
SPage* m_pEndPage; //结束页
SPage* m_pCurrentPage; //当前操作页
unsigned long m_ulPageCount; //页数量
unsigned long m_ulObjectOfPage; //每页中对象的数量
unsigned long m_ulIdleObjectCount; //空闲对象数量
ObjectBuffer_ERROR m_Error; //错误代码
};
//end
//////////////////////////////////
//ObjectBuffer Class template implement
//////////////////////////////////////
template <class T>
ObjectBuffer<T>::ObjectBuffer()
{
m_pBeginPage = NULL; //开始页
m_pEndPage = NULL; //结束页
m_pCurrentPage = NULL; //当前操作页
m_ulPageCount = 0; //页数量
m_ulObjectOfPage = ObjectOfPage_Min; //每页中对象的数量
m_ulIdleObjectCount = 0; //空闲对象数量
m_Error = ObjectBuffer_ERROR_No; //错误标志
m_pCurrentPage = m_pEndPage = m_pBeginPage = new SPage( m_ulObjectOfPage );
if( NULL == m_pBeginPage || m_pBeginPage->m_bMemoryError )
{
m_Error = ObjectBuffer_ERROR_Memory;
return;
}
m_ulPageCount = 1;
m_ulIdleObjectCount += m_ulObjectOfPage;//空闲对象数量增加一个页面的数量
}
template <class T>
ObjectBuffer<T>::ObjectBuffer( unsigned long ulObjectOfPage )
{
m_pBeginPage = NULL; //开始页
m_pEndPage = NULL; //结束页
m_pCurrentPage = NULL; //当前操作页
m_ulPageCount = 0; //页数量
m_ulObjectOfPage = ulObjectOfPage; //每页中对象的数量
m_ulIdleObjectCount = 0; //空闲对象数量
m_Error = ObjectBuffer_ERROR_No; //错误标志
if( m_ulObjectOfPage < ObjectOfPage_Min )m_ulObjectOfPage = ObjectOfPage_Min;
m_pCurrentPage = m_pEndPage = m_pBeginPage = new SPage( m_ulObjectOfPage );
if( NULL == m_pBeginPage || m_pBeginPage->m_bMemoryError )
{
m_Error = ObjectBuffer_ERROR_Memory;
return;
}
m_ulPageCount = 1;
m_ulIdleObjectCount += m_ulObjectOfPage;//空闲对象数量增加一个页面的数量
}
template <class T>
ObjectBuffer<T>::~ObjectBuffer()
{
while( NULL != m_pBeginPage )
{
m_pCurrentPage = m_pBeginPage;
m_pBeginPage = m_pBeginPage->m_pBackPage;
m_pCurrentPage->Release();
delete m_pCurrentPage;
}
m_pBeginPage = NULL; //开始页
m_pEndPage = NULL; //结束页
m_pCurrentPage = NULL; //当前操作页
m_ulPageCount = 0; //页数量
m_ulObjectOfPage = 0; //每页中对象的数量
m_ulIdleObjectCount = 0; //空闲对象数量
m_Error = ObjectBuffer_ERROR_No; //错误标志
}
template <class T>
T* ObjectBuffer<T>::RequestObject()
{
if( m_Error != ObjectBuffer_ERROR_No )return NULL;
if( NULL == m_pCurrentPage )m_pCurrentPage = m_pBeginPage;
for( unsigned long i = 0; i < m_ulPageCount; ++i )
{
if( !m_pCurrentPage->IsEmpty() )//如果不为空就请求一个对象
{
--m_ulIdleObjectCount; //一个对象成功分配,空闲对象数量减1
return m_pCurrentPage->RequestObject();
}
if( m_pCurrentPage == m_pEndPage )//如果到了未尾就从头开始
{
m_pCurrentPage = m_pBeginPage;
}
else
{
m_pCurrentPage = m_pCurrentPage->m_pBackPage;
}
}
//如果循环结束还没找到空闲对象,说明页面已经用完,需要分配新页面
SPage* m_pNew = NULL;
m_pNew = new SPage( m_ulObjectOfPage );
if( NULL == m_pNew || m_pNew->m_bMemoryError )
{
m_Error = ObjectBuffer_ERROR_Memory;
return NULL;
}
m_pNew->m_pFrontPage = m_pEndPage;
m_pEndPage->m_pBackPage = m_pNew;
m_pEndPage = m_pNew;
++m_ulPageCount;
m_pCurrentPage = m_pEndPage;
m_ulIdleObjectCount += m_ulObjectOfPage - 1; //空闲对象数量增加一个页面的数量-1
return m_pCurrentPage->RequestObject();
}
template <class T>
bool ObjectBuffer<T>::ReturnObject( T** ppObject )
{
if( m_Error != ObjectBuffer_ERROR_No)return false;
if( NULL == ppObject || NULL == *ppObject )
{
m_Error = ObjectBuffer_ERROR_NullPointer;
return false;
}
if( NULL == m_pCurrentPage )m_pCurrentPage = m_pBeginPage;
for( unsigned long i = 0; i < m_ulPageCount; ++i )
{
if( m_pCurrentPage->IsMember( *ppObject ) )//对象是否属于此页
{
ObjectBuffer_ERROR Ret = ObjectBuffer_ERROR_No;
if( ObjectBuffer_ERROR_No != ( Ret = m_pCurrentPage->ReturnObject( *ppObject ) ) )
{
m_Error = Ret;
return false;
}
*ppObject = NULL;
++m_ulIdleObjectCount; //一个对象成功归还,空闲对象数量加1
break;
}
if( m_pCurrentPage == m_pEndPage )//如果到了未尾就从头开始
{
m_pCurrentPage = m_pBeginPage;
}
else
{
m_pCurrentPage = m_pCurrentPage->m_pBackPage;
}
}
//如果空闲对象数量大于2倍页面数量,说明空闲对象太多了,需要释放一些以减少内存占用
if( m_ulIdleObjectCount >= ( m_ulObjectOfPage + m_ulObjectOfPage ) )
{
SPage* pTracker = m_pBeginPage; //用于追踪
SPage* pHandlers = NULL; //用于操作
//遍历页面链表,找出空闲页面并释放
while( NULL != pTracker )
{
if( m_ulIdleObjectCount <= m_ulObjectOfPage )break;
pHandlers = pTracker;
pTracker = pTracker->m_pBackPage;//向后遍历
//如果为空闲页面
if( pHandlers->IsFull() )
{
//既不是开始页面也不是结束页面
if( pHandlers != m_pBeginPage && pHandlers != m_pEndPage )
{
pHandlers->m_pFrontPage->m_pBackPage = pHandlers->m_pBackPage;
pHandlers->m_pBackPage->m_pFrontPage = pHandlers->m_pFrontPage;
}
else
{
//如果是开始页面让开始指针指向下一页面
if( pHandlers == m_pBeginPage )
{
m_pBeginPage = pHandlers->m_pBackPage;//让下一页面作为新的开始页面
m_pBeginPage->m_pFrontPage = NULL;//让新开始页面的前一页面指向空
}
else
{
//如果是结束页面让结束指针指向上一页面
m_pEndPage = pHandlers->m_pFrontPage;//让上一页面作为新的结束页面
m_pEndPage->m_pBackPage = NULL;//让新结束页面的后一页面指向空
}
}
//如果和当前页面相同让当前页面指向下一页面
if( pHandlers == m_pCurrentPage )
{
m_pCurrentPage = pHandlers->m_pBackPage;
if( NULL == m_pCurrentPage )m_pCurrentPage = m_pBeginPage; //如果为NULL重新指向开始页面
}
pHandlers->Release();
delete pHandlers;
--m_ulPageCount;
m_ulIdleObjectCount -= m_ulObjectOfPage;
}
}
}
return true;
}
///////////////////////////////
//end
////////////////////////////////////
这是我的测试用例,供大家参考参考:
struct V3D
{
float x, y, z;
};
CTimeSystem t;
char* ErrorInfo[] = { "ObjectBuffer_ERROR_No——没有错误",
"ObjectBuffer_ERROR_Memory——内存分配错误",
"ObjectBuffer_ERROR_NullPointer——无效对象指针",
"ObjectBuffer_ERROR_MultipleReturn——多重归还对象",
};
const int test_count = 300;
int main()
{
t.Initialize();
float BeginTime = 0.0f;
float EndTime = 0.0f;
///*
ObjectBuffer<V3D> VecBuff; // ObjectBuffer<V3D> VecBuff( 400 );
V3D* pvs[test_count] = {NULL};
BeginTime = t.GetTime();
for( int i = 0; i < test_count; i++ )
{
if( NULL == ( pvs = VecBuff.RequestObject() ) )
{
cout<<ErrorInfo[VecBuff.GetError()]<<endl;
return 0;
}
pvs->x = i*10.0f;
pvs->y = i*i+100.0f;
pvs->z = i*i*i - 5000.0f;
}
EndTime = t.GetTime();
cout<<EndTime - BeginTime<<endl;
BeginTime = t.GetTime();
for( i = 0; i < test_count; i++ )
{
if( !VecBuff.ReturnObject( &pvs ) )
{
cout<<ErrorInfo[VecBuff.GetError()]<<endl;
return 0;
}
}
EndTime = t.GetTime();
cout<<EndTime - BeginTime<<endl;
//*/
char c = getchar();
return 0;
}
运行结果如图:
结论:
可见,预知你的需要量,权衡选择你的构造参数,可大副度提高性能。
详述如下:
假设你的最大需要量为1000个对象,而你的构造参数选择了1500,这时所有的操作都在一个页面上完成,速度当然快了。
但假设你选择了一个很小的数如50,这时就要分配很多个页面才能满足你的需要,一些时间就耗费在了换页上,所以速度就慢了。
但如果你高估了你的最大需要量,如你估计为1000个,而实际只需要200个,这时就会有一些内存浪费了。
所以这是一个权衡利弊的数字。
|
|