#ifndef NEW_CACHE #define NEW_CACHE #include #include #include "dslock.h" #define OPT_OBJ_POOL using namespace std; ////////////////////////////////////////////////////////////////////////// //使用以下内存模板管理小对象, from Andrew Kirmse, added by dzj, 06.03.17; //改进:使用每线程的分配与释放缓存, by dzj, 09.04.13; ////////////////////////////////////////////////////////////////////////// /** Copyright (C) Andrew Kirmse, 2001. * All rights reserved worldwide. * * This software is provided "as is" without express or implied * warranties. You may freely copy and compile this source into * applications you distribute provided that the copyright text * below is included in the resulting source code, for example: * "Portions Copyright (C) Andrew Kirmse, 2001" */ #ifndef NEW_CACHE_H #define NEW_CACHE_H /*********************************************************************** ** ** ** TCache ** ** ** ** Objects can be released to the cache in arbitrary order. ** ** ** ***********************************************************************/ /** ///!!!注意:任何类,一旦其某些实例经过了TCache管理,则所有该类对象都必须从TCache管理的池中分配, ///不可自行new,不可定义局部变量,不可定义静态变量,不可memset; ///因为使用了下述的池管理策略,且所有的对象最后都要通过对象池回收,不遵守上述约定会造成内存错误; */ #define PoolFlagDefine \ private:\ unsigned long dwUID;\ bool bIsPoolObj; \ public:\ bool IsStillValid( unsigned long orgUID ) { return (dwUID == orgUID); };\ unsigned long GetUID() { return dwUID; };\ void InitUID() { dwUID = 0ul; };\ void IncUID() { ++ dwUID; };\ void SetPoolFlag() { bIsPoolObj = true; }; \ void ClearPoolFlag() { bIsPoolObj = false; }; \ bool IsPoolObj() { return bIsPoolObj; }; \ void PoolObjInit /////////////////////////////////////////////////////////////////////////////////////////////// // windows实测结果: // windows下改进的r版本对QUEUE_BIAS不敏感,600000时,阈值1时2时间单位,阈值500时1时间单位,阈值2与阈值500相差不大; // windows下原来的r版本,600000时,基本时间单位6-7,比改进版本差了6-7倍; // windows下改进的d版本对QUEUE_BIAS也不敏感,200000时,阈值1时27时间单位,阈值500时16时间单位,阈值2与阈值500相差不大; // windows下原来的d版本,200000时,6-7时间单位,比改进版本(阈值500)好约3倍; // windows下结论:debug版本情况下,未改进版反倒比改进版本好3倍,但release版本下,改进版本要比未改进版本好6-7倍, // 未改进版不能从release中得到太多好处,改进版则可以从release编译中得到十几倍的性能提升 // 另外,对于windows下的改进版,只要阈值大于2,则再加大阈值对优化的影响也不明显。 // // linux实测结果: // linux下改进的r版本对QUEUE_BIAS非常敏感,600000时,阈值1时26时间单位,阈值500时2时间单位; // linux下原来的r版本,600000时,基本时间单位5-6,比改进版本差了3-4倍; // linux下改进的d版本对QUEUE_BIAS非常敏感,200000时,阈值1时15-26时间单位,阈值500时0.5-1时间单位; // linux下原来的d版本,200000时,1-2时间单位; // linux下结论:与其它情形下的比较类似,linux下的debug版本与release版本速度相差不大 // 改进(阈值500)与未改进版本的比较,改进版本总体上要好约3倍 // // windows与linux的比较: // windows改进的r版本(阈值500):1时间单位 ---- linux改进的r版本(阈值500):2时间单位,windows略好,总体基本差不多 // windows改进的d版本(阈值500):16*3时间单位 ---- linux改进的d版本(阈值500):(0.5-1)*4时间单位,linux好一个数量级 // windows下未改进的r版本:6-7时间单位 ---- linux下未改进的r版本:5-6时间单位 // windows下未改进的d版本:(6-7)*3时间单位 ---- linux下未改进的d版本:(1-2)*3时间单位 // // 由于最重要的是两个平台下的release版本性能,而此情形下改进版本性能总体要好3-6倍,因此应该使用改进版本, // 从cpu使用率也可以看出来,改进的r版本cpu使用率约为100%,可能说明两个线程间基本没有相互等待, // 而未改进版本cpu使用率为35-40%,可能说明由于加锁太多两个线程相当时间内都在等待,从而降低了性能。 // 在阈值选择方面,虽然windows版对阈值不太敏感,5或500差别不大,但由于linux版对该值很敏感,因此还是应将此阈值设大,以保证linux下性能 // by dzj, 09.04.14 /////////////////////////////////////////////////////////////////////////////////////////////// #define QUEUE_BIAS 60 template class TNewCache { #ifndef OPT_OBJ_POOL //无池优化时; #define DsRetrieve( notion ) OrgRetrieve( NULL ) #define DsRetrieveOrCreate( notion ) OrgRetrieveOrCreate( NULL ) #define DsRelease( notion, pToRelease ) OrgRelease( pToRelease, NULL ) #else //OPT_OBJ_POOL #define DsRetrieve( notion ) NewRetrieve( GET_TLS_POOL_ALLOC(notion) ) #define DsRetrieveOrCreate( notion ) NewRetrieveOrCreate( GET_TLS_POOL_ALLOC(notion) ) #define DsRelease( notion, pToRelease ) NewRelease( pToRelease, GET_TLS_POOL_RELEASE(notion) ) #endif //OPT_OBJ_POOL private: TNewCache();//屏蔽此操作; public: explicit TNewCache(unsigned int MaxCacheSize) : mMaxCacheSize(MaxCacheSize) { //构造时预先生成好池中的内存结构, by dzj, 07.12.03; m_pArray = NEW T[MaxCacheSize]; if ( NULL != m_pArray ) { for ( unsigned int i=0; i* /*分配缓冲队列,配属于各个线程,优先从此分配*/ ) { T* pItem = NULL; if ( !(mCache.empty()) ) //保护时间尽量短点,只有非空时才进保护区去试图取一个; { DsNewMutex guard(mLock); if (!( mCache.empty() )) { pItem = mCache.front(); mCache.pop_front(); } } if ( NULL != pItem ) { //pItem->IncUID();取的时候不需要增ID号,因为回收的时候已经增了; pItem->PoolObjInit(); } return pItem; } inline T* OrgRetrieveOrCreate( std::list* /*分配缓冲队列,配属于各个线程,优先从此分配*/ ) { T* pItem = NULL; if ( !(mCache.empty()) ) //保护时间尽量短点,只有非空时才进保护区去试图取一个; { DsNewMutex guard(mLock); if (!( mCache.empty() )) { pItem = mCache.front(); mCache.pop_front(); } } if ( NULL == pItem ) //mCache已被取空, NEW一个非池中对象; { pItem = NEW T; if ( NULL != pItem ) { pItem->ClearPoolFlag();//不是初始池中对象; } } if ( NULL != pItem ) { //pItem->IncUID();取的时候不需要增ID号,因为回收的时候已经增了; pItem->PoolObjInit(); } return pItem; } inline void OrgRelease(T* pItem, std::list* /*释放缓冲队列,配属于各个线程,待释放的池中元素先存此队列*/ ) { if ( NULL == pItem ) { return; } if ( !(pItem->IsPoolObj()) ) { //不是池中分配的对象,直接释放; delete pItem; pItem = NULL; } //池中对象回收处理; pItem->IncUID();//递增ID号,以防止指针悬挂; pItem->PoolObjInit(); //释放缓冲队列大小已达到释放阈值; if ( true ) //尽量短地保护mCache; { DsNewMutex guard(mLock); mCache.push_back( pItem ); } } // Returns NULL if no objects available in cache inline T* NewRetrieve( std::list* pInBufList/*分配缓冲队列,配属于各个线程,优先从此分配*/ ) { //恢复使用池,看是否内存仍会增长, T* pItem = NEW T; if ( NULL != pItem ) { pItem->PoolObjInit(); return pItem; } else { return NULL; } //T* pItem = NULL; //if ( NULL == pInBufList ) //{ // return NULL; //} //if ( pInBufList->empty() ) //{ // //如果输入缓冲空,一次性从池中给分配缓冲中添加多个元素。 // if ( !(mCache.empty()) ) //保护时间尽量短点,只有非空时才进保护区去试图取一个; // { // DsNewMutex guard(mLock); // int toallocnum = (int) mCache.size();//当前池中可用元素数目; // toallocnum = (toallocnum > QUEUE_BIAS) ? QUEUE_BIAS:toallocnum;//如果池中有超过指定数目的元素,则分配指定数目元素,否则整个池中的对象都要取到分配缓冲链表中去; // if ( toallocnum <= 0 ) // { // return NULL;//如果外层if ( !(mCache.empty()) )通过,但这里为空,说明进保护区之前被用完,此时仍然返回空; // } // typename std::list::iterator poollisttail = mCache.begin();//准备从链表中取出头上一段放入分配缓冲; // advance( poollisttail, toallocnum ); // pInBufList->splice( pInBufList->end(), mCache, mCache.begin(), poollisttail ); // } //} //if ( !(pInBufList->empty()) ) //尝试从分配缓冲中分配元素; //{ // pItem = pInBufList->front(); // pInBufList->pop_front(); //} //if ( NULL != pItem ) //{ // //pItem->IncUID();取的时候不需要增ID号,因为回收的时候已经增了; // pItem->PoolObjInit(); //} //return pItem; } // Creates a new object on the help if no objects // available in cache inline T* NewRetrieveOrCreate( std::list* pInBufList/*分配缓冲队列,配属于各个线程,优先从此分配*/ ) { //恢复使用池,看是否内存仍会增长 T* pItem = NEW T; if ( NULL != pItem ) { pItem->PoolObjInit(); return pItem; } else { return NULL; } //T* pItem = NULL; //if ( NULL == pInBufList ) //{ // return NULL; //} //if ( pInBufList->empty() ) //{ // //如果输入缓冲空,一次性从池中给分配缓冲中添加多个元素。 // if ( !(mCache.empty()) ) //保护时间尽量短点,只有非空时才进保护区去试图取一个; // { // DsNewMutex guard(mLock); // int toallocnum = (int)mCache.size();//当前池中可用元素数目; // toallocnum = (toallocnum > QUEUE_BIAS) ? QUEUE_BIAS:toallocnum;//如果池中有超过指定数目的元素,则分配指定数目元素,否则整个池中的对象都要取到分配缓冲链表中去; // if ( toallocnum <= 0 ) // { // return NULL;//如果外层if ( !(mCache.empty()) )通过,但这里为空,说明进保护区之前被用完,此时仍然返回空; // } // typename std::list::iterator poollisttail = mCache.begin();//准备从链表中取出头上一段放入分配缓冲; // advance( poollisttail, toallocnum ); // pInBufList->splice( pInBufList->begin(), mCache, mCache.begin(), poollisttail ); // } //} //if ( !(pInBufList->empty()) ) //尝试从分配缓冲中分配一个元素; //{ // pItem = pInBufList->front(); // pInBufList->pop_front(); //} //if ( NULL == pItem ) //mCache已被取空, NEW一个非池中对象; //{ // pItem = NEW T; // if ( NULL != pItem ) // { // pItem->ClearPoolFlag();//不是初始池中对象; // } //} //if ( NULL != pItem ) //{ // //pItem->IncUID();取的时候不需要增ID号,因为回收的时候已经增了; // pItem->PoolObjInit(); //} //return pItem; } inline void NewRelease(T* pItem, std::list* pInBufList/*释放缓冲队列,配属于各个线程,待释放的池中元素先存此队列*/ ) { if ( NULL == pItem ) { return; } //恢复使用池,看是否内存仍会增长,delete pItem; pItem = NULL; return; delete pItem; pItem = NULL; return; //if ( !(pItem->IsPoolObj()) ) //{ // //不是池中分配的对象,直接释放; // delete pItem; pItem = NULL; //} //if ( NULL == pInBufList ) //{ // return; //} ////池中对象回收处理; //pItem->IncUID();//递增ID号,以防止指针悬挂; //pItem->PoolObjInit(); ////池中对象先进释放缓冲队列,累积到一定程度时统一释放; //pInBufList->push_back( pItem ); //if ( pInBufList->size() < QUEUE_BIAS ) //每QUEUE_BIAS个释放一次; //{ // //还不到释放阈值; // return; //} ////释放缓冲队列大小已达到释放阈值; //if ( true ) //尽量短地保护mCache; //{ // DsNewMutex guard(mLock); // mCache.splice( mCache.end(), *pInBufList ); //} } void Clear() { DsNewMutex guard(mLock); if(m_pArray != NULL) { delete[] m_pArray;m_pArray=NULL; } mCache.clear(); } unsigned int GetMaxCacheSize() const { return mMaxCacheSize; } unsigned int GetCurrentCacheSize() const { return mCache.size(); } private: // Private data unsigned int mMaxCacheSize; //std::vector mCache; //改为list,以便给回收到重用之间留开一定的空闲时间,减少过快被重用,导致重用后的物体被错误地认为是重用前的物体这种错误,虽然不能完全杜绝,但至少可以大大减小其发生的概率; std::list mCache; T* m_pArray; DsNewCS mLock; }; #endif // #define NEW_CACHE_H ////////////////////////////////////////////////////////////////////////// //使用以上内存模板管理小对象; ////////////////////////////////////////////////////////////////////////// #endif //NEW_CACHE