FixedAllocator的设计

再参考了《Modern C++ Design》的FixedAllocator的设计,并且优化了一下算法,虽然最坏时间复杂度还是O(N)的,但是在通常情况下,new/delete的使用已经获得了比较好的性能了。

Chunk.h和version1.0的差不多,只是去掉了析构函数,让Chunk直接被FixedAlloctor操作
Chunk.h
#ifndef CHUNK_H
#define  CHUNK_H

#include 
< cassert >

struct  Chunk  {
    
//初始化一个Chunk
    void init(size_t blockSize, unsigned char blocks); 
    
//分配一个block
    void* allocate(size_t blockSize); 
    
//回收一个block
    void deallocate(void* p, size_t blockSize); 
    
//Chunk的起始地址
    unsigned char* pData_; 
    
//第一个可用的block
    unsigned char firstAvailableBlock_; 
    
//block的块数
    unsigned char blocksAvailable_; 
}
;

void  Chunk::init(size_t blockSize, unsigned  char  blocks)  {
    
//从操作系统申请一个Chunk的内存
    pData_ = new unsigned char[blockSize * blocks];
    firstAvailableBlock_ 
= 0;
    blocksAvailable_ 
= blocks;
    unsigned 
char *= pData_;
    
//每个可用的block存放它下一个可用的block的编号
    for (unsigned char i = 1; i < blocks; i++, p += blockSize) {
        
*= i;
    }

}


void *  Chunk::allocate(size_t blockSize)  {
    
if (blocksAvailable_ == 0return 0;
    unsigned 
char* pRet = pData_ + (firstAvailableBlock_ * blockSize);
    
//更新第一个可用的block
    firstAvailableBlock_ = *pRet;
    blocksAvailable_
--;
    
return pRet;
}


void   Chunk::deallocate( void *  p, size_t blockSize)  {
    
//判断回收的地址是否合法
    assert(p >= pData_);
    unsigned 
char* toRel = static_cast<unsigned char*>(p);
    
//判断是否合法
    assert((toRel - pData_) % blockSize == 0);
    
*toRel = firstAvailableBlock_;
    firstAvailableBlock_ 
= static_cast<unsigned char>((toRel - pData_) / blockSize);
    
//判断是否产生精度误差
    assert(firstAvailableBlock_ == ((toRel - pData_) / blockSize));
    blocksAvailable_
++;
}



#endif
FixedAllocator.h
毕竟工程还是工程,很多极端数据都是很难用到的,这个与用户使用习惯有关,对于常用的使用new的习惯(连续new,连续delete,连续new/delete)该FixedAllocator都有比较好的性能。
#ifndef FIXEDALLOCATOR_H
#define  FIXEDALLOCATOR_H

#include 
" Chunk.h "
#include 
< vector >
using   namespace  std;

class  FixedAllocator  {
public :
    FixedAllocator(size_t blockSize);
    
~FixedAllocator();
    
//分配内存
    void* allocate(); 
    
//回收内存
    void deallocate(void* p); 

private :
    
//每个Chunk所含的Block数
    static const int BLOCKS;
    
//block大小
    size_t blockSize_;
    
//该FixedAllocator所含的Chunks
    vector<Chunk> chunks_;
    
//最后一个被用于分配空间的Chunk
    Chunk* lastAllocChunk_;
    
//最后一个被用于释放空间的Chunk
    Chunk* lastDeallocChunk_;
    
//被使用过的Chunks的数
    int numOfUsedChunk_;
    
//判断p是否属于某个chunk
    bool isPtrInChunk(void* p, Chunk* chunk);
}
;

const   int  FixedAllocator::BLOCKS  =   255 ;

FixedAllocator::FixedAllocator(size_t blockSize) 
: blockSize_(blockSize), lastAllocChunk_(
0 ), lastDeallocChunk_( 0 ), numOfUsedChunk_( 0 {}

FixedAllocator::
~ FixedAllocator()  {
    vector
<Chunk>::iterator it = chunks_.begin();
    
for (; it != chunks_.end(); it++{
        delete[] it
->pData_;
    }

}


void *  FixedAllocator::allocate()  {
    
if (!lastAllocChunk_ || !lastAllocChunk_->blocksAvailable_) {
        
//该Chunk不可用,需要搜索新的Chunk,deallocate保证只有vector中的最后一个块为全空
        bool noBlock = true;
        
if (numOfUsedChunk_ < chunks_.size()) {
            vector
<Chunk>::reverse_iterator it = chunks_.rbegin();
            
for (; it != chunks_.rend(); it++{
                
if (it->blocksAvailable_ > 0{
                    lastAllocChunk_ 
= &*it;
                    noBlock 
= false;
                    
break;
                }

            }

        }

        
if (noBlock) {
            
//没有可用Chunk,必须新增一个块
            numOfUsedChunk_++;
            
if (chunks_.size()+1 > chunks_.capacity()) {
                chunks_.reserve(chunks_.capacity() 
+ 1000);
            }

            Chunk newChunk;
            newChunk.init(blockSize_, BLOCKS);
            chunks_.push_back(newChunk);
            lastAllocChunk_ 
= &chunks_.back();
            lastDeallocChunk_ 
= &chunks_.back();
        }

    }

    assert(lastAllocChunk_ 
!= 0);
    assert(lastAllocChunk_
->blocksAvailable_ > 0);
    
return lastAllocChunk_->allocate(blockSize_);
}


inline 
bool  FixedAllocator::isPtrInChunk( void *  p, Chunk *  chunk)  {
    
return (p >= chunk->pData_) && (p < chunk->pData_ + (blockSize_ * BLOCKS));
}


void  FixedAllocator::deallocate( void *  p)  {
    vector
<Chunk>::iterator pChunkToRelease;
    
if (!isPtrInChunk(p, lastDeallocChunk_)) {
        
//要释放的空间不在lastDeallocChunk中
        
//从lastDeallocChunk开始up和down方向查找
        vector<Chunk>::iterator up = lastDeallocChunk_+1;
        vector
<Chunk>::iterator down = lastDeallocChunk_;
        vector
<Chunk>::iterator begVec = chunks_.begin();
        vector
<Chunk>::iterator endVec = chunks_.end();
        
int t = 0;
        
while (down != begVec || up != endVec) {
            t 
^= 1;
            
if (up == endVec) t = 0;
            
if (t) {
                
//up方向
                if (up != endVec) {
                    
if (isPtrInChunk(p, up)) {
                        pChunkToRelease 
= up;
                        
break;
                    }

                    up
++;
                }

            }
 else {
                
//down方向
                if (down != begVec) {
                    down
--;
                    
if (isPtrInChunk(p, down)) {
                        pChunkToRelease 
= down;
                        
break;
                    }

                }

            }

        }

    }
 else {
        pChunkToRelease 
= lastDeallocChunk_;
    }

    
    assert(
&*pChunkToRelease != 0);
    pChunkToRelease
->deallocate(p, blockSize_);
    lastDeallocChunk_ 
= pChunkToRelease;

    
if (pChunkToRelease->blocksAvailable_ == BLOCKS) {
        
//该块已经空
        numOfUsedChunk_--;
        Chunk
* it = &chunks_.back();
        
if (it->blocksAvailable_ == BLOCKS) {
            
//若vector末尾的chunk已是空块,直接把pChunkToRelease删除
            if (it != pChunkToRelease) {
                delete[] pChunkToRelease
->pData_;
                chunks_.erase(pChunkToRelease);
            }

        }
 else {
            
//若vector末尾的chunk非空,把pChunkToRelease移到vector末尾
            Chunk tmp(*pChunkToRelease);
            chunks_.erase(pChunkToRelease);
            chunks_.push_back(tmp);
        }

        lastDeallocChunk_ 
= &chunks_.front();
    }

}


#endif

MemPool.h
比起1.0少了很多代码,这个是当然的,因为很多细节都被封装在FixedAllocator里面,而且还用了STL 的vector,代码又减少了许多,但是测试时候发现vector貌似比自己写的list慢了点,估计原因是那个erase在list里面是O(1)但是 vector是O(n)的。
#ifndef MEMPOOL_H
#define  MEMPOOL_H

#include 
" FixedAllocator.h "

class  MemPool  {
public :
    MemPool(size_t blockSize) : blockSize_(blockSize), allocator_(
new FixedAllocator(blockSize)) {}
    
~MemPool() {
        delete allocator_;
    }

    
void* alloc(size_t size) {
        
if (size != blockSize_) {
            
return ::operator new(size);
        }

        
return allocator_->allocate();
    }

    
void free(void* p, size_t size) {
        
if (!p) return ;
        
if (size != blockSize_) {
            ::
operator delete(p);
        }

        allocator_
->deallocate(p);
    }

private :
    FixedAllocator
* allocator_;
    size_t blockSize_;
}
;


#endif

test.cpp
#include  < iostream >
#include 
" FixedAllocator.h "
#include 
" MemPool.h "
#include 
" time.h "
using   namespace  std;

class  TestClassA  {
public :
    
int a;
    
static void* operator new(size_t size);
    
static void operator delete(void *p, size_t size);
    
static MemPool memPool;
}
;

inline 
void *  TestClassA:: operator   new (size_t size)  {
    
return memPool.alloc(size);
}


inline 
void  TestClassA:: operator  delete( void *  p, size_t size)  {
    memPool.free(p, size);
}


MemPool TestClassA::memPool(
sizeof (TestClassA));

class  TestClassB  {
public :
    
int b;
}
;

const   int  CTIMES  =   1000000 ;

TestClassA
*  pa[CTIMES];
TestClassB
*  pb[CTIMES];

int  main()  {
    
//测试新建1000000个SmallObjet所需要的时间
    int i;
    clock_t begA, begB, endA, endB;
        
    begB 
= clock();
    
for (i=0; i<CTIMES; i++{
        pb[i] 
= new TestClassB;
        }

    endB 
= clock();
    printf(
"Not Used MemPool Time For New = %d ms/n", endB - begB);
    

    begA 
= clock();
    
for (i=0; i<CTIMES; i++{
        pa[i] 
= new TestClassA;
    }


    endA 
= clock();
    printf(
"Used MemPool Time For New = %d ms/n", endA - begA);


    begB 
= clock();
    
for (i=CTIMES-1; i>=0; i--{
        delete pb[i];
    }

    endB 
= clock();
    printf(
"Not Used MemPool Time For Delete = %d ms/n", endB - begB);
    

    begA 
= clock();
    
for (i=0; i<CTIMES; i++{
        delete pa[i];
    }

    endA 
= clock();
    printf(
"Used MemPool Time For Delete = %d ms/n", endA - begA);
    
    
    begB 
= clock();
    
for (i=0; i<CTIMES; i++{
        pb[i] 
= new TestClassB;
        delete pb[i];
    }

    endB 
= clock();
    printf(
"Not Used MemPool Time For New/Delete = %d ms/n", endB - begB);


    begA 
= clock();
    
for (i=0; i<CTIMES; i++{
        pa[i] 
= new TestClassA;
        delete pa[i];
    }

    endA 
= clock();
    printf(
"Used MemPool Time For New/Delete = %d ms/n", endA - begA);


    
return 0;
}


测试结果如下:
Not Used MemPool Time For New = 360 ms
Used MemPool Time For New = 156 ms
Not Used MemPool Time For Delete = 531 ms
Used MemPool Time For Delete = 266 ms
Not Used MemPool Time For New/Delete = 906 ms
Used MemPool Time For New/Delete = 344 ms
Press any key to continue

明显比version1.0好很多,接着准备写versiong1.2,希望获得更好的封装,同时优化再优化FixedAllocator的算法。还有推荐大家看《Modern C++ Design》这本书,真的很好。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
关于Frmresize1.1的说明: FRMRESIZE VERSION 1.1 (CLASS) ---------------------- 作者: SUNVFP Mail: SUNVFP@EYOU.COM 开发日期:2002.10.27 改进日期:2002.10.29 简介: ------------ 有许多狐友关心不同分辨率下表单的显示问题和改变表单大小时表单内各控件的相应改变问题, 也有相当多的狐友写过这方面的类。 我所见过的resize类在使用中都有大大小小的不如人意之处: 如:vfp自己的resize类示例,不支持嵌套等;老外resize类,需要fll动态链接库支持,并且当 控件数量超过256时就会出错等。还有算法不严密,造成控件失位,Frmresize1.0就有这方面的BUG。 我决定重新写一个,代码很简单。大家不妨试用一下,并请不吝提出要求,以方便改进,使之成 为通用类,使后来者少走弯路。 1)Frmresize可以改变字体的相应大小; 2)Frmresize没有嵌套层数的限制,没有控件数量的限制; 3)Frmresize可以有选择的控制各控件是否改变,及如何改变; 4)Frmresize可以自适应不同分辨率。 改进: —————————— 1)增加NoHeightClassList 、NoHeightObjectList 两个属性,可以控制控件的高度是否变化; 2)改进算法,彻底消除失位现象。不论如何调整表单大小,控件的相对位置都不会失位!; 3)因算法的变化,去掉两个不再需要的自定义属性。 用法: ----- 要使用 Frmresize 1.1 必须按以下两步: 添加该类到你的表单, 并设置它的属性. 1) 将 Frmresize 类拖放到你的表单 2) 设置它的属性: 设置类的 FontResize 属性 (在属性窗口的末端) (.T.(默认值)-字体随着作相应大小的改变,.F.-字体大小不变) 设置类的 NoPosClassList 属性 (在属性窗口的末端) (不作位置变化的基类列表,例如commandbutton text等,timer custom hyperlink已排除) 设置类的 NoSizeClassList 属性 (不作大小变化的基类列表,例如commandbutton text等,timer custom hyperlink已排除) 设置类的 NoPosObjectList 属性 (不作位置变化的控件名称列表,例如txt1 COMMAND1 CMDOK等) 设置类的 NoSizeObjectList 属性 (不作大小变化的控件名称列表,例如txt1 COMMAND1 CMDOK等) 设置类的 NoHeightClassList 属性 (不作高度变化的基类列表,例如commandbutton text等,timer custom hyperlink已排除) 设置类的 NoHeightObjectList 属性 (不作高度变化的控件名称列表,例如txt1 COMMAND1 CMDOK等) 注:1。当NoPosClassList和NoSizeClassList 或 NoPosObjectList和NoSizeObjectList中同时包含时,则相关控件及其下层控件不变化。 2。NoSizeClassList或NoSizeObjectList中的相应控件的字体大小不变。 3) 在表单resize事件中写:this.frmresize1.frmresize() 注: 不带任何参数。 4) 在表单init事件中写:this.frmresize1.frmresize(800,600) 注: 参数800,600是指你开发时的屏幕分辨率。 许可: 该类可以没有问题地添加到你的项目. 我要求的唯一东西是如果你改进了程序请让我知道. 发送 Bug 和建议到: sunvfp@eyou.com 或到天堂论坛进行讨论 www.myf1.net

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值