经典内存池c++源代码分享及性能对比研究

会有一股神秘感。 提交于 2019-11-30 09:28:07

最近在写一个IOCP扩展类,遇到内存池问题,网上的完整代码不多,可惜没钱下载,只能自己研究了,自己写了份代码,实现了基本功能,自动增加内存块,可实现较大吞吐量。代码分享出来,请大家交流,帮助优化。目前还只能实现单个对象的管理,大神请指点。

先来看性能对比结果,见下图(代码在后面,可能微博识别的原因导致贴上来后代码有些错误,自己调整):
Release模式下,反复循环处理总计5200万个对象,传统new/delete操作花4.29秒,内存池操作花2.1秒,大约是传统new/delete一半时间(若测试方案不同,测试规模增大,速度优势越明显)。
本例中,内部维护了64个块(每个块5mb),总计320Mb,可用单元524万个单元,已用了161万个单元,内存池的内存使用情况和结构由dumpInfo输出,
在这里插入图片描述

在这里插入图片描述

头文件:

#pragma once

#ifndef __CL_OBJMEMPOOL_H__
#define __CL_OBJMEMPOOL_H__

//内部锁类,基类
class CLCSLock
{
public:
    CLCSLock(void);
    ~CLCSLock(void);
    //主锁,请用于线程队列的增加删除操作
    void lock();
    void unLock();

protected:
private:
    CRITICAL_SECTION *m_cs = 0;
};

template <</span>typename classTag>
class CLObjMenBlock;

template <</span>typename classTag>
class CLObjMenPool;

//内存池对象单元模板类
template <</span>typename classTag>
class CLObjMenUnitInfo
{
    friend class CLObjMenPool;
    friend class CLObjMenBlock;
    friend class CLObjMenUnitInfo;
public:
    classTag data;
protected:
    CLObjMenUnitInfo *pPre;
    CLObjMenUnitInfo *pNext;
    CLObjMenBlock *pThisBlock;
    //在构造时候显示调用
    CLObjMenUnitInfo *init(
        CLObjMenBlock *_pThisBlock = 0,
        CLObjMenUnitInfo *_pPre = 0,
        CLObjMenUnitInfo *_pNext = 0)
    {
        pThisBlock = _pThisBlock;
        pPre = _pPre;
        pNext = _pNext;
        return this;
    }
};

//内存池内存块对象模板类
template <</span>typename classTag>
class CLObjMenBlock
{
    friend class CLObjMenPool;
    friend class CLObjMenBlock;
    friend class CLObjMenUnitInfo;
protected:
    CLObjMenBlock(DWORD_PTR PerBlockMemCapacity_MB)
    {
        init();
        alloc(PerBlockMemCapacity_MB * 1024 * 1024 / sizeof(CLObjMenUnitInfo));
    }
    ~CLObjMenBlock()
    {
        release();
        init();
    }
    //判断该内存块是否存在可用单元
    inline BOOL isUsable()
    {
        return pUsableLst ? TRUE : FALSE;
    }
    //将对象指针回收进入内存块单元
    CLObjMenUnitInfo *freeOneData(CLObjMenUnitInfo *pUnit)
    {
        assert(pUnit != NULL);
        //执行对象析构
        (&(pUnit->data))->classTag::~classTag();
        return putToUsable(pUnit);
    }
    //取出一个待命可用的对象单元,返回其构造后的对象指针
    classTag *getOneData()
    {
        assert(pUsableLst != NULL);
        //执行对象默认构造
        pUsableLst->CLObjMenUnitInfo::CLObjMenUnitInfo();
        return putToUnusable(pUsableLst);
    }

    inline void init()
    {
        pUsableLst = 0;
        nMaxUsable = 0;
        pUnusableLst = 0;
        nMaxUnusable = 0;
        pPreBlock = 0;
        pNextBlock = 0;
        pMainDataLst = 0;
        nMaxDataCounts = 0;
    }
    //分配内存
    void alloc(DWORD_PTR unitCounts = 1)
    {
        assert(pMainDataLst == 0 && pUsableLst == 0 && pUnusableLst == 0);
        if (pMainDataLst == 0)
        {
            //第一次申请内存空间
            pMainDataLst = (CLObjMenUnitInfo *)malloc(sizeof(CLObjMenUnitInfo) * (nMaxDataCounts = nMaxUsable = (unitCounts == 0 ? 1 : unitCounts)));
            //第一次执行可用队列的初始化连接工作
            pUsableLst = &pMainDataLst[0];
            pMainDataLst[0].init(this, 0, nMaxDataCounts <= 1 ? 0 : &pMainDataLst[1]);
            for (DWORD_PTR i = 1; i < nMaxDataCounts - 1; i++)
            {
                pMainDataLst[i].init(this, &pMainDataLst[i - 1], &pMainDataLst[i + 1]);
            }
            pMainDataLst[nMaxDataCounts - 1].init(this, nMaxDataCounts <= 1 ? 0 : &pMainDataLst[nMaxDataCounts - 2], 0);
        }
    };
    //释放内存
    void release()
    {
        if (pMainDataLst)
        {
            //析构所有已使用的对象
            for (CLObjMenUnitInfo *pc = pUnusableLst; pc != NULL; )
            {
                pUnusableLst = freeOneData(pc);
                pc->init();
                pc = pUnusableLst;
            }
            //释放动态内存
            free(pMainDataLst);
            pMainDataLst = 0;
        }
    }
    //对象放入可用队列头,返回不可用队列头指针
    CLObjMenUnitInfo  *putToUsable(CLObjMenUnitInfo *pUnit)
    {
        assert(this == pUnit->pThisBlock);
        //处理不可用列表头
        if (pUnusableLst && pUnusableLst == pUnit)
            pUnusableLst = pUnit->pNext;

        //处理前后连接,隔离对象
        extruct(pUnit);

        //接入可用列表
        if (pUsableLst)
        {
            pUsableLst->pPre = pUnit;
        }
        pUnit->pNext = pUsableLst;
        pUnit->pPre = 0;
        pUsableLst = pUnit;
        nMaxUnusable -= 1;
        nMaxUsable += 1;
        return pUnusableLst;

    }
    //返回可用对象指针
    classTag  *putToUnusable(CLObjMenUnitInfo *pUnit)
    {
        assert(this == pUnit->pThisBlock);
        //处理可用列表头
        if (pUsableLst && pUsableLst == pUnit)
            pUsableLst = pUnit->pNext;

        //处理前后连接,隔离对象
        extruct(pUnit);

        //接入不可用列表
        if (pUnusableLst)
        {
            pUnusableLst->pPre = pUnit;
        }
        pUnit->pNext = pUnusableLst;
        pUnit->pPre = 0;
        pUnusableLst = pUnit;
        nMaxUnusable += 1;
        nMaxUsable -= 1;
        return &pUnit->data;
    }
    //处理前后连接,隔离对象
    inline void extruct(CLObjMenUnitInfo *pUnit)
    {
        if (pUnit->pPre)
        {
            pUnit->pPre->pNext = pUnit->pNext;
        }
        if (pUnit->pNext)
            pUnit->pNext->pPre = pUnit->pPre;
    }


    CLObjMenUnitInfo *pMainDataLst;
    DWORD_PTR nMaxDataCounts;
    CLObjMenUnitInfo *pUsableLst;
    DWORD_PTR nMaxUsable;
    CLObjMenUnitInfo *pUnusableLst;
    DWORD_PTR nMaxUnusable;
    CLObjMenBlock *pPreBlock;
    CLObjMenBlock *pNextBlock;
};

//内存池对象模板类
template <</span>typename classTag>
class CLObjMenPool
{
    friend class CLObjMenPool;
    friend class CLObjMenBlock;
    friend class CLObjMenUnitInfo;
public:
    CLObjMenPool(DWORD_PTR PerBlockMemCapacity_MB = 5)
    {
        setPerBlockMemCapacity(PerBlockMemCapacity_MB);
        pEntry = 0;
        pCurrentUsingBlock = 0;
    }
    ~CLObjMenPool()
    {
        for (CLObjMenBlock *pi = pEntry; pi != NULL; )
        {
            pCurrentUsingBlock = pi->pNextBlock;
            delete pi; // 释放块
            pi = pCurrentUsingBlock;
        }
        m_PerBlockMemCapacity = 0;
        pEntry = 0;
        pCurrentUsingBlock = 0;
    }
    //向内存池动态申请一个对象,构造并返回其指针
    classTag *newOne()
    {
        if (pEntry == NULL)
        {
            pCurrentUsingBlock = pEntry = new CLObjMenBlock(m_PerBlockMemCapacity);
        }
        CLObjMenBlock *pStartBlock = pCurrentUsingBlock;
        //检索可用的块
        for (; pCurrentUsingBlock->isUsable() == FALSE;)
        {
            if (pCurrentUsingBlock->pNextBlock == NULL)
            {
                if (pCurrentUsingBlock != pEntry)
                {
                    pCurrentUsingBlock = pEntry;
                }
                else
                {
                    pStartBlock = new CLObjMenBlock(m_PerBlockMemCapacity);
                    if (pEntry->pNextBlock)
                        pEntry->pNextBlock->pPreBlock = pStartBlock;
                    pStartBlock->pNextBlock = pEntry->pNextBlock;
                    pEntry->pNextBlock = pStartBlock;
                    pCurrentUsingBlock = pStartBlock;
                }
            }
            else if (pCurrentUsingBlock->pNextBlock == pStartBlock)  //插入新块,在队列中
            {
                pStartBlock = new CLObjMenBlock(m_PerBlockMemCapacity);
                if (pCurrentUsingBlock->pNextBlock)
                    pCurrentUsingBlock->pNextBlock->pPreBlock = pStartBlock;
                pStartBlock->pNextBlock = pCurrentUsingBlock->pNextBlock;
                pCurrentUsingBlock->pNextBlock = pStartBlock;
                pCurrentUsingBlock = pStartBlock;
            }
            else pCurrentUsingBlock = pCurrentUsingBlock->pNextBlock;
        }
        return pCurrentUsingBlock->getOneData();
    }
    //向内存池释放一个动态申请的对象,进行相关析构操作
    void deleteOne(CLObjMenUnitInfo *pDelete)
    {
        assert(pDelete->pThisBlock != NULL);
        pDelete->pThisBlock->freeOneData(pDelete);
    }
    //设置单个内存块的大小,单位MB,默认5MB
    void setPerBlockMemCapacity(DWORD_PTR PerBlockMemCapacity_MB = 5)
    {
        m_PerBlockMemCapacity = PerBlockMemCapacity_MB == 0 ? 1 : PerBlockMemCapacity_MB;
    }
    //统计内存使用量,并控制向控台输出当前内存池的内存使用情况。
    //bLog = TRUE表示输出信息到控制台否则只统计内存使用量,bDeTail = FALSE表示采用简化输出
    DWORD_PTR dumpInfo(BOOL bLog = TRUE, BOOL bDeTail = FALSE)
    {
        if (bLog)
        {
            if (bDeTail)
                _tprintf_s(_T("\n>>The Memmery pool dumper detail--------------------------------- \n\n>>MemBlock Info:\n"));
            else
                _tprintf_s(_T("\n>>The Memmery pool dumper simple--------------------------------- "));
        }
        size_t si = 0;
        size_t siu = 0;
        size_t sit = 0;
        for (const CLObjMenBlock *pc = pEntry; pc; )
        {
            si++;
            siu += pc->nMaxUnusable;
            sit += pc->nMaxDataCounts;
            size_t n = pc->nMaxDataCounts == 0 ? 0 : ((double)pc->nMaxUnusable) / pc->nMaxDataCounts * 50.0;
            if (bLog && bDeTail)
            {
                _tprintf_s(_T(">>MemBlock(%zd): ["), si);
                for (size_t i = 0; i < 50; i++)
                {
                    if (i < n)_tprintf_s(_T("*"));
                    else _tprintf_s(_T("-"));
                }
                _tprintf_s(_T("] \n"), n * 2);
            }
            pc = pc->pNextBlock;
        }
        DWORD_PTR perObj = sizeof(CLObjMenUnitInfo);
        DWORD_PTR mem = perObj * sit + si * sizeof(CLObjMenBlock);
        if (bLog)
        {
            DWORD_PTR Tb = 0, Gb = 0, Mb = 0, Kb = 0, Byte = 0;
            Kb = mem / 1024;
            Byte = mem % 1024;
            Mb = Kb / 1024;
            Kb = Kb % 1024;
            Gb = Mb / 1024;
            Mb = Mb % 1024;
            Tb = Gb / 1024;
            Gb = Gb % 1024;
            if (Tb > 0)
                _tprintf_s(_T("\n>>Summary: mem=%zdT %zdG %zdM %zdK %zdB, blocks=%zd, total=%zd, used=%zd.\n"), Tb, Gb, Mb, Kb, Byte,  si, sit, siu);
            else if (Gb > 0)
                _tprintf_s(_T("\n>>Summary: mem=%zdG %zdM %zdK %zdB, blocks=%zd, total=%zd, used=%zd.\n"), Gb, Mb, Kb, Byte,  si, sit, siu);
            else if (Mb > 0)
                _tprintf_s(_T("\n>>Summary: mem=%zdM %zdK %zdB, blocks=%zd, total=%zd, used=%zd.\n"), Mb, Kb, Byte,  si, sit, siu);
            else if (Kb > 0)
                _tprintf_s(_T("\n>>Summary: mem=%zdK %zdB, blocks=%zd ,total=%zd, used=%zd.\n"), Kb, Byte,  si, sit, siu);
            else
                _tprintf_s(_T("\n>>Summary: mem=%zdB, blocks=%zd, total=%zd, used=%zd.\n"), Byte,  si, sit, siu);
        }
        return mem;
    }
protected:
    CLObjMenBlock *pEntry;
    CLObjMenBlock *pCurrentUsingBlock;
    DWORD_PTR m_PerBlockMemCapacity;
};

//全局的安全锁
extern CLCSLock _g_memPoolGLock;
//全局的安全锁
inline CLCSLock &getGlobleMemPoolLocker()
{
    return _g_memPoolGLock;
}
//取得类型对应的内存池对象
template <</span>typename classTag>
CLObjMenPool *getMenPool()
{
    static CLObjMenPool _MenPool;
    return &_MenPool;
}
//向内存池动态申请一个对象,返回对象指针。对应的,该指针必须用deleteOne释放
template <</span>typename classTag>
classTag *newOne()
{
    getGlobleMemPoolLocker().lock();
    classTag *rt = getMenPool()->newOne();
    getGlobleMemPoolLocker().unLock();
    assert(rt != NULL);
    return rt;

}
//将由newOne获得的对象指针释放回内存池,会做相关析构操作
template <</span>typename classTag>
void deleteOne(classTag *pDelete = 0)
{
    assert(pDelete != NULL);
    getGlobleMemPoolLocker().lock();
    getMenPool()->deleteOne((CLObjMenUnitInfo *)pDelete);
    getGlobleMemPoolLocker().unLock();
}
//设置内存池中内存块当个最大内存占用大小,单位MB
template <</span>typename classTag>
void setMemPoolBlockCapacity(DWORD_PTR mb = 5 )
{
    assert(mb != 0);
    getGlobleMemPoolLocker().lock();
    getMenPool()->setPerBlockMemCapacity(mb);
    getGlobleMemPoolLocker().unLock();
}

#endif

cpp文件:

#include"CLObjectMemPool.h"

//全局唯一的线程池锁对象
CLCSLock _g_memPoolGLock;

CLCSLock::CLCSLock(void)
{
    if (m_cs == 0)
    {
        m_cs = new CRITICAL_SECTION;
        if (m_cs) InitializeCriticalSection(m_cs);
        else throw std::logic_error("CriticalSection didn't alloc!");
    }
}

CLCSLock::~CLCSLock(void)
{
    if (m_cs)
    {
        DeleteCriticalSection(m_cs);
        delete m_cs;
        m_cs = 0;
    }
}

void CLCSLock::lock()
{
    if(m_cs)EnterCriticalSection(m_cs);
    else throw std::logic_error("CriticalSection didn't alloc!");
}

void CLCSLock::unLock()
{
    if (m_cs)LeaveCriticalSection(m_cs);
    else throw std::logic_error("CriticalSection didn't alloc!");
}

测试代码,其中CLString是我自定义的一个类:

int main()  // BHV test
{
    _tprintf_s(_T("\n Mem Pool Test: "));

    size_t mb = 200;//单次连续申请内存200 MB
    size_t cycleTimes = 10;//循环次数
    size_t si = (double)mb * 1024 * 1024 / sizeof(CLString);

    PCLString *plst = new PCLString[si];
    ZeroMemory(plst, si * sizeof(PCLString));

    ULONGLONG st = GetTickCount64();
    for (size_t j = 0; j < cycleTimes; j++)
    {
        for (size_t i = 0; i < si; i++)
        {
            if (plst[i] == 0) plst[i] = new CLString;
        }
        for (size_t i = 0; i < si; i++)
        {
            if (plst[i] != 0)delete plst[i];
            plst[i] = 0;;
        }
    }
    ULONGLONG et = GetTickCount64();

    ZeroMemory(plst, si * sizeof(PCLString));

    //设置单个块最大10Mb
    //setMemPoolBlockCapacity(10);
    ULONGLONG st2 = GetTickCount64();
    for (size_t j = 0; j < cycleTimes; j++)
    {
        for (size_t i = 0; i < si; i++)
        {
            if (plst[i] == 0)plst[i] = newOne();
        }
        for (size_t i = 0; i < si; i++)
        {
            if (plst[i] != 0)deleteOne(plst[i]);
            plst[i] = 0;
        }
    }
    ULONGLONG et2 = GetTickCount64();

    _tprintf_s(_T("\n\n对比结果: 处理对象数 = %zd, new/delete操作 = %zdms, MemPool操作 = %zdms. \n\n"), si * cycleTimes, et - st, et2 - st2);

    for (size_t i = 0; i < si / 3.25; i++)
    {
        if (plst[i] == 0)plst[i] = newOne();
    }
    getMenPool()->dumpInfo(1, 1); //采用详细输出
    for (size_t i = 0; i < si / 3.25; i++)
    {
        if (plst[i] != 0)deleteOne(plst[i]);
        plst[i] = 0;
    }
    delete[] plst;
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!