内存分配(4)–LOKI的小物件分配器

 

今天我们来看看Loki的内存分配.有一本书叫做<Mordern C++ Design>,个人认为是一本非常不错的书,值得一读。Loki的内存分配目的很明确,loki的设计者认为当前的C运行库的内存分配函数(malloc/realloc/free)并没有针对小内存分配做过优化,导致在有大量小内存频繁分配释放的环境下性能很差,所以他要解决这个问题,这一点倒是和C++ STL很相似,只不过loki的设计者更露骨,他在给分配器起名字的时候也充分强调了loki的内存分配是针对小物件做了大量工作的。
对比一下SGI STL,有一个类,维护了一个单向链表,该链表的每个节点指向负责固定大小内存的空闲链表。loki的思想也是这样的,看一下这个类(为了解释起来方便,我做了一点简化)
   class SmallObjAllocator
    {
    public:
        SmallObjAllocator(std::size_t maxObjectSize);
   
        void* Allocate(std::size_t numBytes);
        void  Deallocate(void* p, std::size_t size);
   
    private:
               
        std::vector<FixedAllocator> pool_;
        std::size_t maxObjectSize_;
    };
其实SmallObjAllocator管理的东西和STL的__default_alloc_template管理的东西是一样的,这里的vector<FixedAllocator>就相当于STL里面的m_free_list[16]。FixedAllocator就负责某一固定大小的内存的分配释放。STL中如果内存大小大于128字节,就交给第一级分配器处理,loki中给用户一点自由,你指定一个大小吧,如果将来申请的内存大小大于 maxObjectSize,就直接交给C运行库的malloc/free处理.
还有一点很大的差别,SGI STL的设计者人为的把小内存分成16个类别,以8个字节为单位,要浪费就稍微浪费一点点。loki的设计者连这点都不愿意,那最好就是把在内部有 maxObjectSize个FixedAllocator罗,这样给定一个大小,就可以在常数时间pool_[i](i是内存大小)找到负责的那个FixedAllocator,问题是如果,maxObjectSize是128,但是用户只申请大小为4和64字节的内存,那有126个FixedAllocator就浪费了。所以loki的设计者就认为只有在第一次有用户申请大小为n的内存的时候,才创建负责大小n的FixedAllocator,然后把它加到pool_中去。听上去不错哦,可是这样一来,接着来一个大小为m的申请,我怎么知道大小为m的FixedAllocator已经存在了呢,如果存在,我怎么能以最快的时间在pool_里面找到它呢。那这样好了,我每次创建一个新的FixedAllocator往pool_加的时候都按照他们所管理的内存大小从小到大排号序,来请求的时候,我做一个二分查找,虽然达不到常数时间,但是也不会很差的。
作为一个库的设计者,因该力求效率高,loki的设计者不满足于简单的二分查找的效率,他们要更快,怎么办呢,大家还记得访问局部性的假设吗?loki的设计者觉得阿,如果你上次分配了一个大小为n的内存,你很有可能接下来也是分配大小为n的内存,在释放内存的时候也是有同样的希望。于是再加两个成员变量:
private:
     FixedAllocator* pLastAlloc_;      // 指向上次用于分配内存的那个FixedAllocator
     FixedAllocator* pLastDealloc_;    // 指向上次用于释放内存的那个FixedAllocator
现在来看看分配内存的源代码:
void* SmallObjAllocator::Allocate(std::size_t numBytes)
{
    if (numBytes > maxObjectSize_) return operator new(numBytes);
   
    //如果上次缓存的那个FixedAllocator能行就最好了
    if (pLastAlloc_ && pLastAlloc_->BlockSize() == numBytes)
    {
        return pLastAlloc_->Allocate();
    }
    //如果上次缓存的FixedAllocator不行,只能用二分查找法找
    Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes);
    if (i == pool_.end() || i->BlockSize() != numBytes) //如果没有找到符合要求的
  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值