游戏开发论坛

 找回密码
 立即注册
搜索
查看: 4249|回复: 2

小弟精心打造的一个对象缓冲器-1.1版

[复制链接]

45

主题

157

帖子

169

积分

注册会员

Rank: 2

积分
169
QQ
发表于 2009-8-9 11:46:00 | 显示全部楼层 |阅读模式
   
    改进了重复归还检测的算法,让归还对象的速度提升到与申请对象相同的级别上,增加了归还指针的合法性检测,完善了错误处理机制,基本能达到工业强度的水平了。现和大家分享分享,有用的尽管拿去,秉着开源精神,决不收版权费,但得回声一下,必竟是我一周的心血。



/*************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个,这时就会有一些内存浪费了。
所以这是一个权衡利弊的数字。

15

主题

368

帖子

406

积分

中级会员

Rank: 3Rank: 3

积分
406
发表于 2009-8-12 09:26:00 | 显示全部楼层

Re:小弟精心打造的一个对象缓冲器-1.1版

开源行为值得支持!




另需要注意的一些地方:

1.不要直接new T,而是通过new sizeof(T)大小的内存。构造函数的触发只在它应该触发的时机。
2.考虑内存字节对齐
3.考虑线程安全

0

主题

6

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2009-8-19 11:26:00 | 显示全部楼层

Re: 小弟精心打造的一个对象缓冲器-1.1版

支持下毕竟原创. 对象缓冲器有点不专业,应该叫ObjectPool显得专业点嘛,O(∩_∩)O哈哈~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-8-7 16:30

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表