Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

mempool.h

Go to the documentation of this file.
00001 /** @file mempool.h */
00002 /* 
00003  * Copyright (C) 2002 Laird Breyer
00004  *  
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  * 
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  * 
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018  * 
00019  * Author:   Laird Breyer <laird@lbreyer.com>
00020  */
00021 
00022 #ifndef _MEM_POOL_H_
00023 #define _MEM_POOL_H_
00024 
00025 #include "basictypes.h"
00026 #include <cassert>
00027 #include <stdexcept>
00028 #include <stdlib.h>
00029 #include <new>
00030 #include <iostream>
00031 
00032 /// Wraps around a class T and redefines new() and delete()
00033 /// member functions.
00034 /** Allocates chunks of BLOCK_SIZE objects at a time, then
00035  *  gives them out and recycles them. 
00036  */
00037 template<class T>
00038 class MemoryPooled
00039 {
00040  public:
00041   MemoryPooled() {} // default constructor does nothing
00042   ~MemoryPooled() {} // we'll never call this :-)
00043  
00044   static void* operator new(size_t size); //size_t size);
00045   static void operator delete(void *dead, size_t size);
00046 
00047   static int FreeBlocks();
00048 
00049  protected:
00050   union {
00051     T data;
00052     MemoryPooled<T> *next;
00053   };
00054 
00055  private:
00056 
00057   static const int BLOCK_SIZE;
00058   static MemoryPooled<T>* allocate_block();
00059   static MemoryPooled<T> *freelist;
00060 };
00061 
00062 /// Allocate a single object
00063 template<class T>
00064 void* MemoryPooled<T> :: operator new(size_t size) {
00065 
00066   if( size != sizeof(MemoryPooled<T>)) { 
00067     cerr << "mempool: allocated object of nonstandard size" << endl;
00068     return ::operator new(size); 
00069   }
00070   MemoryPooled<T> *p = freelist;
00071   if(p) { 
00072       freelist = p->next;
00073   } else {
00074     assert(BLOCK_SIZE > 1);
00075     p = allocate_block();
00076     freelist = p->next;
00077     assert(freelist);
00078   }
00079   assert(p);
00080   return p;
00081 }
00082 
00083 /// Utility function
00084 template<class T>
00085 MemoryPooled<T>* MemoryPooled<T> :: allocate_block() {
00086   MemoryPooled<T> *newBlock = 
00087     static_cast<MemoryPooled<T>*>(::operator new(BLOCK_SIZE * 
00088                                                  sizeof(MemoryPooled<T>)));
00089   // this memset is so it shows up in top
00090   memset(newBlock,0,BLOCK_SIZE * sizeof(MemoryPooled<T>));
00091   for(int i = 0; i < BLOCK_SIZE - 1; i++) {
00092     newBlock[i].next = &newBlock[i+1];
00093   }
00094   newBlock[BLOCK_SIZE - 1].next = 0;
00095   return newBlock;
00096 }
00097 
00098 /// Recycle a single object
00099 template<class T>
00100 void MemoryPooled<T> :: operator delete(void *dead, size_t size) {
00101 
00102   if( dead == 0 ) { 
00103     return; 
00104   }
00105   if( size != sizeof(MemoryPooled<T>) ) {
00106     cerr << "warning: overloaded delete called global delete" << endl;
00107    :: operator delete(dead);
00108     return;
00109   }
00110   MemoryPooled<T> *shell = static_cast<MemoryPooled<T>*>(dead);
00111   shell->next = freelist;
00112   freelist = shell;
00113 }
00114 
00115 
00116 template<class T>
00117 int MemoryPooled<T> :: FreeBlocks() {
00118   //  cout << "begin freeblocks1" << endl;
00119   MemoryPooled<T> *p = freelist;
00120   int fsize = 0;
00121   while( p ) {
00122     fsize += sizeof(MemoryPooled<T>);
00123     p = p->next;
00124   }
00125   return fsize;
00126 }
00127 
00128 //==========================================//
00129 
00130 /// A memory management wrapper around a class S.
00131 /**
00132  * The wrapper adds two admin data members to S, as a union.
00133  * These members allow the class MemPool<S> to keep track
00134  * of free allocated memory blocks in a linked list.
00135  * 
00136  * If you need a large number of small (one to two element)
00137  * arrays of type S, wrap S as a MemPoolObject<S> and allocate
00138  * memory using a MemPool<S> object. Not useful if sizeof(S) < 4.
00139  */
00140 template<class S>
00141 union MemPoolObject {
00142   S data;
00143   MemPoolObject<S>* admin_next;
00144   int admin_length;
00145 };
00146 
00147 /// A memory managed class suitable for many small arrays of type S.
00148 /**
00149  * This memory pool class handles memory management for large
00150  * numbers of small arrays whose elements are of type S.
00151  * The normal new[] and delete[] functions for S objects 
00152  * introduce much
00153  * overhead, so they aren't suitable for the "fromlinks" and "tolinks"
00154  * Link arrays associated with each WebNode. Instead, MemPool<S> 
00155  * provides Allocate() and Deallocate() member functions to replace
00156  * new[] and delete[]. 
00157  */
00158 template<class S>
00159 class MemPool
00160 {
00161 
00162  public:
00163   MemPool() {} // default constructor does nothing
00164   ~MemPool() {} // we'll never call this :-)
00165  
00166   MemPoolObject<S>* Allocate(int numels) throw (range_error);
00167   void Deallocate(MemPoolObject<S>* dead, int numels);
00168 
00169   static int FreeBlocks1();
00170   static int FreeBlocks2();
00171  private:
00172 
00173   static const int BLOCK_SIZE;
00174   static MemPoolObject<S>* allocate_block1();
00175   static MemPoolObject<S>* allocate_block2();
00176 
00177   static MemPoolObject<S> *mempool1; ///< mempool1 contains only single chunks
00178   static MemPoolObject<S> *mempool2; ///< mempool2 contains blocks of chunks suitable for arrays
00179 };
00180 
00181 template<class S>
00182 MemPoolObject<S>* MemPool<S> :: Allocate(int numels) throw (range_error) {
00183   if( (numels <= 0) ) {
00184     cerr << "warning: overloaded new[] called global new" << endl;;
00185     return static_cast<MemPoolObject<S>*>(::operator new[](numels * sizeof(MemPoolObject<S>)));
00186   } else if( numels == 1 ) {
00187     // too small for freelist2
00188     MemPoolObject<S> *p = mempool1;
00189     if(p) { 
00190       mempool1 = p[0].admin_next; 
00191     } else {
00192       p = allocate_block1();
00193       assert(p);
00194       mempool1 = p[0].admin_next;
00195     }
00196     return p;
00197   }
00198 
00199   // each request now is for at least two elements
00200   // find the first block big enough
00201   MemPoolObject<S> *p = mempool2;
00202   if(p) {
00203     // find first chunk that's big enough for us
00204     MemPoolObject<S> *prev = NULL;
00205     while( p && (p[1].admin_length < numels) ) {
00206       prev = p;
00207       p = p[0].admin_next;
00208     }
00209     if( p ) { // found one
00210       // remove p block from the freelist2
00211       if( prev ) {
00212         prev[0].admin_next = p[0].admin_next;
00213       } else {
00214         mempool2 = p[0].admin_next;
00215       }
00216 
00217       // prepend the rest of the block minus numels
00218       // to freelist2, or freelist1 if it's too small
00219       int remaining = p[1].admin_length - numels;
00220       if( remaining == 1 ) {
00221         //cout << "mucking with freelist1" << endl;
00222         p[numels].admin_next = mempool1;
00223         mempool1 = &p[numels];
00224       } else if( remaining > 1 ) {
00225         p[numels].admin_next = mempool2;
00226         p[numels + 1].admin_length = remaining;
00227         mempool2 = &p[numels];
00228       }
00229       // return the p block
00230       return p;
00231     }
00232   }
00233 
00234   // if we're here, then we must allocate a new block
00235   assert(BLOCK_SIZE > 1); // don't want no riffraff here
00236   if(numels > BLOCK_SIZE) {
00237     cerr << "BLOCK_SIZE is too small" << endl;
00238     throw range_error("");
00239   } else {
00240     p = allocate_block2();
00241     // attach remaining block to the freelist
00242     int remaining = BLOCK_SIZE - numels;
00243     if( remaining == 1 ) { 
00244       p[numels].admin_next = mempool1;
00245       mempool1 = &p[numels];
00246     } else if( remaining > 1 ) {
00247         p[numels].admin_next = mempool2;
00248         p[numels + 1].admin_length = remaining;
00249         mempool2 = &p[numels];
00250     }
00251     return p;
00252   }
00253 }
00254 
00255 template<class S>
00256 MemPoolObject<S>* MemPool<S> :: allocate_block1() {
00257   MemPoolObject<S> *newBlock = 
00258     static_cast<MemPoolObject<S>*>(::operator new(BLOCK_SIZE * sizeof(MemPoolObject<S>)));
00259   // this memset is so it shows up in top
00260   memset(newBlock,0,BLOCK_SIZE * sizeof(MemPoolObject<S>));
00261   for(int i = 0; i < BLOCK_SIZE - 1; i++) {
00262     newBlock[i].admin_next = &newBlock[i+1];
00263   }
00264   newBlock[BLOCK_SIZE - 1].admin_next = 0;
00265   return newBlock;
00266 }
00267 
00268 template<class S>
00269 MemPoolObject<S>* MemPool<S> :: allocate_block2() {
00270   MemPoolObject<S> *newBlock = 
00271     static_cast<MemPoolObject<S>*>(::operator new(BLOCK_SIZE * sizeof(MemPoolObject<S>)));
00272   memset(newBlock,0,BLOCK_SIZE * sizeof(MemPoolObject<S>));
00273   newBlock[0].admin_next = 0;
00274   newBlock[1].admin_length = BLOCK_SIZE;
00275   return newBlock;
00276 }
00277 
00278 template<class S>
00279 int MemPool<S> :: FreeBlocks1() {
00280   MemPoolObject<S> *p = mempool1;
00281   int fsize = 0;
00282   while( p ) {
00283     fsize += sizeof(MemPoolObject<S>);
00284     p = p[0].admin_next;
00285   }
00286   return fsize;
00287 }
00288 
00289 template<class S>
00290 int MemPool<S> :: FreeBlocks2() {
00291   MemPoolObject<S> *p = mempool2;
00292   int fsize = 0;
00293   while( p ) {
00294     fsize += p[1].admin_length * sizeof(MemPoolObject<S>);
00295     p = p[0].admin_next;
00296   }
00297   return fsize;
00298 }
00299 
00300 template<class S>
00301 void MemPool<S> :: Deallocate(MemPoolObject<S> *dead, int numels) {
00302   if( numels == 1 ) {
00303     dead[0].admin_next = mempool1;
00304     mempool1 = dead;
00305   } else if( numels > 1 ) {
00306     // order is important
00307     dead[1].admin_length = numels;
00308     dead[0].admin_next = mempool2;
00309     mempool2 = dead;
00310   }
00311 }
00312 
00313 #endif

Generated on Wed May 29 11:37:14 2002 for MarkovPR by doxygen1.2.15