SmallObjectAllocator实作

本文介绍了一种自定义内存分配器的实现方法,包括简单分配器和小对象分配器。简单分配器直接使用标准库的new和delete操作符进行内存分配与释放;小对象分配器则采用固定大小的内存块进行分配,通过缓存和链表提高分配效率。

#ifndef _ALLOC_H_
#define _ALLOC_H_
#pragma once

#include <new>
#include "Assert.h"

#ifndef NULL
#define NULL 0
#endif

namespace KOK
{
    class __CSimpleAlloc
    {
    public:
        static void* Allocate(size_t n)
        {
            if(n != 0) return ::operator new(n);
        }

        static void Deallocate(void* p)
        {
            if(p != NULL) return ::operator delete(p);
        }

        static void Deallocate(void* p, size_t n)
        {
            if(p != NULL) return ::operator delete(p);
        }
    };

    class __CSmallObjectAlloc
    {
    private:
        struct CChunk
        {
            unsigned char* pData;//the real chunk address
            unsigned char availableBlockIndex; //first available block index
            unsigned char availableBlockCount; //the maximun block count is 255
            CChunk* pNext;

            CChunk(unsigned char blockCount, size_t blockSize)
            {
                availableBlockCount = blockCount;
                availableBlockIndex = 0;
                pNext = NULL;
                unsigned char* pDataTemp = pData = (unsigned char*)malloc(blockCount * blockSize);
                unsigned char i = 0;
                while(i ^ blockCount) //initial next available index, act as a signal list
                {
                    *pDataTemp = (++i);
                    pDataTemp += blockSize;
                }
            }

            ~CChunk()
            {
                free(pData);
            }

            inline bool IsAvailable()
            {
                return availableBlockCount != 0;
            }

            //locate the block specified by pointer p
            inline unsigned char GetBlockIndex(unsigned char* p, unsigned blockSize)
            {
                KOK_ASSERT((p != NULL) && (p >= pData) && ((p - pData) % blockSize == 0));

                return (p - pData)/blockSize;

            }

            //alloc, make blocks link each other like a signal list
            unsigned char* GetBlock(unsigned blockSize)
            {
                KOK_ASSERT(availableBlockCount != 0);
                availableBlockCount--;
                unsigned char pTemp = availableBlockIndex;
                if(availableBlockCount)
                {
                    availableBlockIndex = *(pData + blockSize * availableBlockIndex);
                }

                return pData + blockSize * pTemp;
            }

            //dealloc, add free block to the front of slist
            void PutBlock(unsigned char* p, unsigned blockSize)
            {
                KOK_ASSERT((p != NULL) && (p >= pData) && ((p - pData) % blockSize == 0) );
                availableBlockCount++;
                *p = availableBlockIndex;
                availableBlockIndex = GetBlockIndex(p, blockSize);

            }

        };

        struct CFixAlloc
        {
            unsigned char blockCount; //block count, 255 is max.
            size_t blockSize; //block size, in gerneral it doesnt exceed 128k
            unsigned availableChunkCount; //available chunk count, which is uesed to justify if fix allocator leaves one available chunk at least.
            CChunk* pFirst;//chunk list
            CChunk* pCurFreePrev;
            CChunk* pCurFree;//free cache
            CChunk* pCurAlloc;//alloc cache
            CFixAlloc* pNext;

        private:
            inline CChunk* NewChunk()
            {
                return new CChunk(blockCount, blockSize);
            }

            //locate the chunk specified by pointer p
            inline bool IsInChunk(unsigned char* p, CChunk* pChunk)
            {
                KOK_ASSERT(p != NULL);
                return p >= pChunk->pData && p <= pChunk->pData + blockCount * blockSize;
            }

            void PutBlockAux(unsigned char* p, CChunk*& pChunk, CChunk*& pChunkPrev)
            {
                pChunk->PutBlock(p, blockSize);

                if(pChunk->availableBlockCount == 1) //in the condition of put a block from full used chunk, note while blockCount == 1
                    availableChunkCount++;

                if(pChunk->availableBlockCount == blockCount) //all the blocks are free
                {
                    if(availableChunkCount > 1) //leave one available chunk at least(exclude itself)
                    {
                        if(pChunk == pChunkPrev) //in the condition of first chunk would be freed
                            pFirst = pChunk->pNext;
                        else
                            pChunkPrev->pNext = pChunk->pNext; //remove all-blocks free chunk from slist
                        free(pChunk); //free the chunk
                        if(pChunk == pCurAlloc) //update cache accordingly
                            pCurAlloc = NULL;
                        pChunk = NULL;
                        availableChunkCount--;
                    }
                }

            }

        public:
            CFixAlloc(unsigned blockSize, unsigned char blockCount = 255)
            {
                this->blockSize = blockSize;
                this->blockCount = blockCount;
                availableChunkCount = 0;
                this->pNext = NULL;
                this->pFirst = this->pCurAlloc = this->pCurFree = this->pCurFreePrev = NULL;
            }

            ~CFixAlloc()
            {
                CChunk* pTemp = NULL;
                while(pFirst != NULL)
                {
                    pTemp = pFirst;
                    pFirst = pFirst->pNext;
                    delete pTemp;
                }
            }
            void PutBlock(unsigned char* p)
            {
                KOK_ASSERT(p != NULL);
                if(pCurFree != NULL &&  IsInChunk(p, pCurFree)) //case 1: cache hits
                {
                    PutBlockAux(p, pCurFree, pCurFreePrev);
                }
                else //case 2: search the approciate chunk from head of link
                {
                    CChunk* pTargetChunk = pFirst;
                    CChunk* pTargetChunkPrev = pFirst;
                    while(pTargetChunk != NULL)
                    {
                        if(IsInChunk(p, pTargetChunk))
                        {
                            PutBlockAux(p, pTargetChunk, pTargetChunkPrev);
                            break;
                        }
                        else
                        {
                            pTargetChunkPrev = pTargetChunk;
                            pTargetChunk= pTargetChunk->pNext;
                        }
                    }
                    pCurFree = pTargetChunk; //update free cache
                    pCurFreePrev = pTargetChunkPrev;
                }
            }

            //alloc
            unsigned char* GetBlock()
            {
                unsigned char* pReturn;
                CChunk* pAvailableChunk = pFirst;
                if(pCurAlloc != NULL && pCurAlloc->IsAvailable()) //case 1: cache hits
                {
                    pReturn = pCurAlloc->GetBlock(blockSize);
                    if(!pCurAlloc->IsAvailable())
                        availableChunkCount--;
                }
                else //case 2: find the first available chunk from head of link
                {
                    while(pAvailableChunk != NULL)
                    {
                        if(pAvailableChunk->IsAvailable())
                        {
                            pReturn = pAvailableChunk->GetBlock(blockSize);

                            if(!pAvailableChunk->IsAvailable())
                                availableChunkCount--;
                            break;
                        }
                        else
                        {
                            pAvailableChunk = pAvailableChunk->pNext;
                        }
                    }
                    if(pAvailableChunk == NULL) //case 3: if all the chunks are used, alloc a new chunk
                    {
                        pAvailableChunk = NewChunk();
                        pAvailableChunk->pNext = pFirst; //add a new chunk to the front of slist
                        pFirst = pAvailableChunk;
                        pReturn = pAvailableChunk->GetBlock(blockSize);
                        if(pAvailableChunk->IsAvailable())// in condition of blockcount == 1
                            availableChunkCount++;
                    }
                    pCurAlloc = pAvailableChunk;//update cache
                }
                return pReturn;
            }

        };

    private:
        CFixAlloc* m_pFirst_; //fix allocator link
        CFixAlloc* m_pCurAlloc_; //cache
        CFixAlloc* m_pCurFree_; //cache
        size_t m_nAlign_;
        size_t m_blockCount_; //number of blocks per chunk

    protected:
        inline bool IsInFixAlloc(size_t n, CFixAlloc* pAlloc)
        {
            return n == pAlloc->blockSize;
        }

    public:
        __CSmallObjectAlloc(size_t nAlign = sizeof(char*), size_t nBlockCount = 255)
        {
            m_pFirst_ = m_pCurAlloc_ = m_pCurFree_ = NULL;
            m_nAlign_ = nAlign;
            m_blockCount_ = nBlockCount;
        }

        ~__CSmallObjectAlloc()
        {
            CFixAlloc* pTemp = NULL;
            while(m_pFirst_ != NULL)
            {
                pTemp = m_pFirst_;
                m_pFirst_ = m_pFirst_->pNext;
                delete pTemp;
            }
        }

        void* Allocate(size_t n)
        {
            n = (n + m_nAlign_ - 1) & ~(m_nAlign_ - 1); // align

            if(n > 128) //big object allocator
                return ::operator new(n);
            else //small object allocator
            {
                if(m_pCurAlloc_ != NULL && IsInFixAlloc(n, m_pCurAlloc_))
                {
                    return m_pCurAlloc_->GetBlock();
                }
                else
                {
                    CFixAlloc* pTargetFixAlloc = m_pFirst_;
                    while(pTargetFixAlloc != NULL)
                    {
                        if(IsInFixAlloc(n, pTargetFixAlloc))
                        {
                            m_pCurAlloc_ = pTargetFixAlloc;//update cache
                            return pTargetFixAlloc->GetBlock();
                        }
                        else
                            pTargetFixAlloc = pTargetFixAlloc->pNext;
                    }
                    if(pTargetFixAlloc == NULL)
                    {
                        pTargetFixAlloc = new CFixAlloc(n,m_blockCount_);
                        pTargetFixAlloc->pNext = m_pFirst_;
                        m_pFirst_ = m_pCurAlloc_ = pTargetFixAlloc; //update cache
                        return pTargetFixAlloc->GetBlock();
                    }
                }
            }
        }
        void Deallocate(void* p, size_t n)
        {
            KOK_ASSERT(p != NULL);
            n = (n + m_nAlign_ - 1) & ~(m_nAlign_ - 1);//align

            if(n > 128)
            {
                ::operator delete(p);
                return;
            }

            if(m_pCurFree_ != NULL && IsInFixAlloc(n, m_pCurFree_))
            {
                m_pCurFree_->PutBlock((unsigned char*)p);
            }
            else
            {
                CFixAlloc* pTargetFixAlloc = m_pFirst_;
                while(pTargetFixAlloc != NULL)
                {
                    if(IsInFixAlloc(n, pTargetFixAlloc))
                    {
                        m_pCurFree_ = pTargetFixAlloc; //update cache
                        pTargetFixAlloc->PutBlock((unsigned char*)p);
                        break;
                    }
                    else
                        pTargetFixAlloc = pTargetFixAlloc->pNext;
                }

                KOK_ASSERT(pTargetFixAlloc != NULL);
            }
            return;
        }
    };

    class CSmallObjectAlloc
    {
    private:
        CSmallObjectAlloc();
        CSmallObjectAlloc(const CSmallObjectAlloc&);
        CSmallObjectAlloc& operator=(const CSmallObjectAlloc&);

    private:
        static __CSmallObjectAlloc objectAllocator;

    public:
        static void* Allocate(size_t n)
        {
            return objectAllocator.Allocate(n);
        }

        static void Deallocate(void* p, size_t n)
        {
            objectAllocator.Deallocate(p, n);
        }

    };

    __CSmallObjectAlloc CSmallObjectAlloc::objectAllocator;

    template <class T, class Value>
    inline void Construct(T* p, Value& value)
    {
        new(p) T(value);
    }

    template <class T, class Value>
    inline void Construct(T* p)
    {
        new(p) T();
    }

    template <class T>
    inline void Destroy(T* p)
    {
        p->~T();
    }

    #define _USE_CUSTOM_ALLOC_
    #ifndef _USE_CUSTOM_ALLOC_
    typedef __CSimpleAlloc alloc;
    #else
    typedef CSmallObjectAlloc alloc;
    #endif

    template <class T, class Alloc = alloc>
    class CAllocAdapter
    {
    public:
        static T* Allocate(size_t n)
        {
            return n == 0 ? 0 : (T*)Alloc::Allocate(n * sizeof(T));
        }

        static T* Allocate(void)
        {
            return (T*)Alloc::Allocate(sizeof(T));
        }

        static void Deallocate(T* p)
        {
            Alloc::Deallocate(p, sizeof(T));
        }

        static void Deallocate(T* p, size_t n)
        {
            Alloc::Deallocate(p, n);
        }

    };
}
#endif

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值