在高效C++编程中看到一个不错的内存池实现方案,这里共享下,大家看看有什么不足。
代码很简单,如下:
template<typename T>
class CMemoryPool
{
    public:
        enum { EXPANSION_SIZE = 32};

        CMemoryPool(unsigned int nItemCount = EXPANSION_SIZE)
        {
            ExpandFreeList(nItemCount);
        }
        
        ~CMemoryPool()
        {
            //free all memory in the list
            CMemoryPool<T>* pNext = NULL;
            for(pNext = m_pFreeList; pNext != NULL; pNext = m_pFreeList)
            {
                m_pFreeList = m_pFreeList->m_pFreeList;
                delete [](char*)pNext;
            }
        }

        void* Alloc(unsigned int /*size*/)
        {
            if(m_pFreeList == NULL)
            {
                ExpandFreeList();
            }
            
            //get free memory from head
            CMemoryPool<T>* pHead = m_pFreeList;
            m_pFreeList = m_pFreeList->m_pFreeList;
            return pHead;
        }

        void Free(void* p)
        {
            //push the free memory back to list
            CMemoryPool<T>* pHead = static_cast<CMemoryPool<T>*>(p);
            pHead->m_pFreeList = m_pFreeList;
            m_pFreeList = pHead;
        }

    protected:
        //allocate memory and push to the list
        void ExpandFreeList(unsigned nItemCount = EXPANSION_SIZE)
        {
            unsigned int nSize = sizeof(T) > sizeof(CMemoryPool<T>*) ? sizeof(T) : sizeof(CMemoryPool<T>*);
            CMemoryPool<T>* pLastItem = static_cast<CMemoryPool<T>*>(static_cast<void*>(new char[nSize]));
            m_pFreeList = pLastItem;
            for(int i=0; i<nItemCount-1; ++i)
            {
                pLastItem->m_pFreeList = static_cast<CMemoryPool<T>*>(static_cast<void*>(new char[nSize]));
                pLastItem = pLastItem->m_pFreeList;
            }

            pLastItem->m_pFreeList = NULL;
        }

    private:
        CMemoryPool<T>* m_pFreeList;
};

它的实现思想就是每次从List的头上取内存, 如果取不到则重新分配一定数量; 用完后把内存放回List头部,这样的话效率很高,因为每次List上可以取到的话,肯定是空闲的内存。

当然上面的代码只是针对单线程的,要支持多线程的话也很简单,外面加一层就可以了,
代码如下:
class CCriticalSection
{
public:
    CCriticalSection()
    {
        InitializeCriticalSection(&m_cs);
    }

    ~CCriticalSection()
    {
        DeleteCriticalSection(&m_cs);
    }

    void Lock()
    {
        EnterCriticalSection(&m_cs); 
    }

    void Unlock()
    {
        LeaveCriticalSection(&m_cs);
    }

protected:
    CRITICAL_SECTION m_cs;
};

template<typename POOLTYPE, typename LOCKTYPE>
class CMTMemoryPool
{
    public:
        void* Alloc(unsigned int size)
        {
            void* p = NULL;
            m_lock.Lock();
            p = m_pool.Alloc(size);
            m_lock.Unlock();

            return p;
        }

        void Free(void* p)
        {
            m_lock.Lock();
            m_pool.Free(p);
            m_lock.Unlock();    
        }

    private:
        POOLTYPE m_pool;
        LOCKTYPE m_lock;
};

这是我的测试代码:
#include <iostream>
#include <windows.h>

using namespace std;

#include "MemoryPool.h"
#include "MTMemoryPool.h"

class CTest
{
public:
    int m_n;
    int m_n1;

    voidoperator new(size_t size)
    {
        void* p = s_pool->Alloc(size);
        return p;
    }

    void operator delete(void* p, size_t size)
    {
        s_pool->Free(p);
    }

    static void NewPool()
    {
        //s_pool = new CMemoryPool<CTest>;
        s_pool = new CMTMemoryPool<CMemoryPool<CTest>, CCriticalSection>;
    }

    static void DeletePool()
    {
        delete s_pool;
        s_pool = NULL;
    }
    
    //static CMemoryPool<CTest>* s_pool;
    static CMTMemoryPool<CMemoryPool<CTest>, CCriticalSection>* s_pool;
};

//CMemoryPool<CTest>* CTest::s_pool = NULL;
CMTMemoryPool<CMemoryPool<CTest>, CCriticalSection>* CTest::s_pool = NULL;

void testFun()
{
    int i;
    const int nLoop = 10;
    const int nCount = 10000;
    
    for(int j = 0; j<nLoop; ++j)
    {
        typedef CTest* LPTest;
        LPTest arData[nCount];
        for(i=0;i <nCount; ++i)
        {
            arData[i] = new CTest;
        }

        for(i=0;i <nCount; ++i)
        {
            delete arData[i];
        }
    }
}

int main(int argc, char* argv[])
{
    {
        unsigned int dwStartTickCount = GetTickCount();

        CTest::NewPool();

        testFun();
        
        CTest::DeletePool();
        
        cout << "total cost" << GetTickCount() - dwStartTickCount << endl;
    }


    system("pause");

    return 0;
}
在我机器上测试结果比系统默认的CRT实现高效N倍。
posted on 2012-05-05 23:23 Richard Wei 阅读(18329) 评论(12)  编辑 收藏 引用 所属分类: C++

FeedBack:
# re: 一个高效的内存池实现
2012-05-05 23:55 | zgpxgame
固定大小的缓冲池策略  回复  更多评论
  
# re: 一个高效的内存池实现
2012-05-06 09:35 | Richard Wei
@zgpxgame
是的,适用经常分配和释放的同一类型的对象。
可变对象大小的内存池还要复杂的多,也没有很通用的解决方案。
  回复  更多评论
  
# re: 一个高效的内存池实现
2012-05-07 08:39 | Richard Wei
@ithaca
不好意思,有点误导,应该是这本书,
《提高C++性能的编程技术》  回复  更多评论
  
# re: 一个高效的内存池实现
2012-06-13 00:23 | 华夏之火
对于c++类中的这种通过operator new,来使用特制的内存池的接口方式,我咋觉得很难受。如果类的内存池可由用户来指定,那就很好了。不过我思考了很久,一直都找不到好的方式,实现出来的效果,总是使用的接口太难看了。最后,我心一横,需要内存分配的对象,全部都是POD类型的,没有构造析构,这样用户想要指定什么内存池,就用什么内存池  回复  更多评论
  
# re: 一个高效的内存池实现
2012-06-19 17:51 | alias
用配置文件来指定对象大小和数量,稍微解决。  回复  更多评论
  
# re: 一个高效的内存池实现
2013-07-29 10:39 | foundwant
为什么我测试的还不如系统分配的速度快呢?
int main(int argc,char*argv[])
{
unsigned int dwStartTickCount = GetTickCount();
CTest::NewPool();
testFun();
CTest::DeletePool();
cout << "total cost:" << GetTickCount()-dwStartTickCount <<endl;


dwStartTickCount = GetTickCount();
// CTest::NewPool();
for(int i = 0;i<10;++i)
{
int* array[10000];
for(int j = 0;j<10000;++j)
{
array[j] = new int;
}
for(int j = 0; j<10000;++j)
{
delete array[j];
}
}
// CTest::DeletePool();
cout << "System Cost:"<< GetTickCount() - dwStartTickCount << endl;

system("pause");
return 0;
}  回复  更多评论
  
# re: 一个高效的内存池实现
2013-07-29 17:56 | Richard Wei
@foundwant
确实, 这和分配策略有关。
上面的内存池适合频繁的分配和释放的情况, 但是对于多次连续分配就不适合了。其他一些内存池可参考:http://www.cppblog.com/weiym/archive/2013/04/08/199238.html  回复  更多评论
  
# re: 一个高效的内存池实现
2014-05-09 13:53 | xzq0102@163.com
上述代码有个Bug:
unsigned int nSize = sizeof( T ) > sizeof( CMemoryPool<T>* ) ? sizeof( T ) : sizeof( CMemoryPool<T>* );

应改为:

unsigned int nSize = sizeof( T ) > sizeof( CMemoryPool<T>) ? sizeof( T ) : sizeof( CMemoryPool<T>* );

  回复  更多评论
  
# re: 一个高效的内存池实现
2014-09-23 21:43 | duanyuncanyang
事实上你的CMemoryPool代码是有问题的,如果T是一个对象,你上面的代码没有办法调用T的构造函数来构造对象,而是使用强制转化的方式。所以对于T中有虚函数或者是虚继承过来的,不调用构造函数编译器将无法正确设置vptr,所以T的对象在调用虚函数的时候会发生错误。所以可以这样改,
T* Alloc(unsigned int /*size*/)
{
if(m_pFreeList == NULL)
{
ExpandFreeList();
}

//get free memory from head
CMemoryPool<T>* pHead = m_pFreeList;
m_pFreeList = m_pFreeList->m_pFreeList;
return new (pHead) T;
}  回复  更多评论
  
# re: 一个高效的内存池实现
2014-09-23 22:09 | Richard Wei
@duanyuncanyang
memory pool 本身只负责内存分配,是给对象的operate new 和operate delete调用的,具体参见上面的测试代码  回复  更多评论
  
# re: 一个高效的内存池实现
2015-04-22 18:54 | 方贵深
@duanyuncanyang不会吧,他这个函数只是返回一块内存,最外面的new还会在这块内存上调用构造函数,这个应该没错。  回复  更多评论
  
# re: 一个高效的内存池实现
2015-06-27 20:14 | GD
unsigned int nSize = sizeof(T) > sizeof(CMemoryPool<T>*) ? sizeof(T) : sizeof(CMemoryPool<T>*);

这句的用意是什么?

还有为啥不转成static_cast<byte*>(pList)  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理