objcache.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  *
00003  * $Id: classlist.cpp,v 1.14 2001/03/19 19:27:39 root Exp $
00004  *
00005  * Copyright (C) 1997-2008 by Dimitri van Heesch.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and its
00008  * documentation under the terms of the GNU General Public License is hereby 
00009  * granted. No representations are made about the suitability of this software 
00010  * for any purpose. It is provided "as is" without express or implied warranty.
00011  * See the GNU General Public License for more details.
00012  *
00013  * Documents produced by Doxygen are derivative works derived from the
00014  * input used in their production; they are not affected by this license.
00015  *
00016  */
00017 
00018 #include <stdio.h>
00019 #include <assert.h>
00020 #include <qglobal.h>
00021 #include "objcache.h"
00022 
00023 //----------------------------------------------------------------------
00024 
00025 #ifdef CACHE_STATS
00026 int ObjCache::misses = 0;
00027 int ObjCache::hits   = 0;
00028 #endif
00029 
00030 //----------------------------------------------------------------------
00031 
00032 ObjCache::ObjCache(unsigned int logSize) 
00033   : m_head(-1), m_tail(-1), //m_numEntries(0), 
00034     m_size(1<<logSize), m_freeHashNodes(0), m_freeCacheNodes(0), m_lastHandle(-1)
00035 {
00036   int i;
00037   m_cache = new CacheNode[m_size];
00038   m_hash  = new HashNode[m_size];
00039   // add all items to list of free buckets
00040   for (i=0;i<m_size-1;i++)
00041   {
00042     m_hash[i].nextHash = i+1;
00043     m_cache[i].next    = i+1;
00044   }
00045 }
00046 
00047 ObjCache::~ObjCache()
00048 {
00049   delete[] m_cache;
00050   delete[] m_hash;
00051 }
00052 
00053 int ObjCache::add(void *obj,void **victim)
00054 {
00055   *victim=0;
00056 
00057   HashNode *hnode = hashFind(obj);
00058   //printf("hnode=%p\n",hnode);
00059   if (hnode) // move object to the front of the LRU list, since it is used
00060     // most recently
00061   {
00062     //printf("moveToFront=%d\n",hnode->index);
00063     moveToFront(hnode->index);
00064 #ifdef CACHE_STATS
00065     hits++;
00066 #endif
00067   }
00068   else // object not in the cache.
00069   {
00070     void *lruObj=0;
00071     if (m_freeCacheNodes!=-1) // cache not full -> add element to the cache
00072     {
00073       // remove element from free list
00074       int index = m_freeCacheNodes;
00075       m_freeCacheNodes = m_cache[index].next;
00076 
00077       // add to head of the list
00078       if (m_tail==-1)
00079       {
00080         m_tail = index;
00081       }
00082       m_cache[index].prev = -1;
00083       m_cache[index].next = m_head;
00084       if (m_head!=-1)
00085       {
00086         m_cache[m_head].prev = index;
00087       }
00088       m_head = index;
00089     }
00090     else // cache full -> replace element in the cache
00091     {
00092       //printf("Cache full!\n");
00093       lruObj = m_cache[m_tail].obj;
00094       hashRemove(lruObj);
00095       moveToFront(m_tail); // m_tail indexes the emptied element, which becomes m_head
00096     }
00097     //printf("numEntries=%d size=%d\n",m_numEntries,m_size);
00098     m_cache[m_head].obj = obj;
00099     hnode = hashInsert(obj);
00100     hnode->index = m_head;
00101     *victim = lruObj;
00102 #ifdef CACHE_STATS
00103     misses++;
00104 #endif
00105   }
00106   return m_head;
00107 }
00108 
00109 void ObjCache::del(int index)
00110 {
00111   assert(index!=-1);
00112   assert(m_cache[index].obj!=0);
00113   hashRemove(m_cache[index].obj);
00114   moveToFront(index);
00115   m_head = m_cache[index].next;
00116   if (m_head==-1) 
00117     m_tail=-1;
00118   else 
00119     m_cache[m_head].prev=-1;
00120   m_cache[index].obj=0;
00121   m_cache[index].prev=-1;
00122   m_cache[index].next = m_freeCacheNodes;
00123   m_freeCacheNodes = index;
00124 }
00125 
00126 #ifdef CACHE_DEBUG
00127 #define cache_debug_printf printf
00128 void ObjCache::printLRU()
00129 {
00130   cache_debug_printf("MRU->LRU: ");
00131   int index = m_head;
00132   while (index!=-1)
00133   {
00134     cache_debug_printf("%d=%p ",index,m_cache[index].obj);
00135     index = m_cache[index].next;
00136   }
00137   cache_debug_printf("\n");
00138 
00139   cache_debug_printf("LRU->MRU: ");
00140   index = m_tail;
00141   while (index!=-1)
00142   {
00143     cache_debug_printf("%d=%p ",index,m_cache[index].obj);
00144     index = m_cache[index].prev;
00145   }
00146   cache_debug_printf("\n");
00147 }
00148 #endif
00149 
00150 #ifdef CACHE_STATS
00151 #define cache_stats_printf printf
00152 void ObjCache::printStats()
00153 {
00154   cache_stats_printf("ObjCache: hits=%d misses=%d hit ratio=%f\n",hits,misses,hits*100.0/(hits+misses));
00155 }
00156 #endif
00157 
00158 void ObjCache::moveToFront(int index)
00159 {
00160   int prev,next;
00161   if (m_head!=index)
00162   {
00163     next = m_cache[index].next;
00164     prev = m_cache[index].prev;
00165 
00166     // de-chain node at index
00167     m_cache[prev].next = next;
00168     if (next!=-1) m_cache[next].prev = prev; else m_tail = prev;
00169 
00170     // add to head
00171     m_cache[index].prev  = -1;
00172     m_cache[index].next  = m_head;
00173     m_cache[m_head].prev = index;
00174     m_head = index;
00175   }
00176 }
00177 
00178 unsigned int ObjCache::hash(void *addr)
00179 {
00180   static bool isPtr64 = sizeof(addr)==8;
00181   if (isPtr64)
00182   {
00183     uint64 key = (uint64)addr;
00184     // Thomas Wang's 64 bit Mix Function
00185     key += ~(key << 32);
00186     key ^=  (key >> 22);
00187     key += ~(key << 13);
00188     key ^=  (key >> 8);
00189     key +=  (key << 3);
00190     key ^=  (key >> 15);
00191     key += ~(key << 27);
00192     key ^=  (key >> 31);
00193     return key & (m_size-1);
00194   }
00195   else
00196   {
00197     // Thomas Wang's 32 bit Mix Function
00198     unsigned long key = (unsigned long)addr;
00199     key += ~(key << 15);
00200     key ^=  (key >> 10);
00201     key +=  (key << 3);
00202     key ^=  (key >> 6);
00203     key += ~(key << 11);
00204     key ^=  (key >> 16);
00205     return key & (m_size-1);
00206   }
00207 }
00208 
00209 ObjCache::HashNode *ObjCache::hashFind(void *obj)
00210 {
00211   HashNode *node = 0;
00212   int index = m_hash[hash(obj)].head;
00213   //printf("hashFind: obj=%p index=%d\n",obj,index);
00214   while (index!=-1 &&
00215       m_hash[index].obj!=obj
00216       ) // search for right object in the list
00217   {
00218     index = m_hash[index].nextHash;
00219   }
00220   // found the obj at index, so it is in the cache!
00221   if (index!=-1)
00222   {
00223     node = &m_hash[index];
00224   }
00225   return node;
00226 }
00227 
00228 ObjCache::HashNode *ObjCache::hashInsert(void *obj)
00229 {
00230   int index = hash(obj);
00231   //printf("Inserting %p index=%d\n",obj,index);
00232 
00233   // remove element from empty list
00234   int newElement = m_freeHashNodes;
00235   assert(newElement!=-1);
00236   m_freeHashNodes = m_hash[m_freeHashNodes].nextHash;
00237 
00238   if (m_hash[index].head!=-1) // hash collision -> goto end of the list
00239   {
00240     index = m_hash[index].head;
00241     while (m_hash[index].nextHash!=-1)
00242     {
00243       index = m_hash[index].nextHash;
00244     }
00245     // add to end of the list
00246     m_hash[index].nextHash = newElement;
00247   }
00248   else // first element in the hash list
00249   {
00250     m_hash[index].head = newElement;
00251   }
00252   // add to the end of the list
00253   m_hash[newElement].nextHash = -1;
00254   m_hash[newElement].obj = obj;
00255   return &m_hash[newElement];
00256 }
00257 
00258 void ObjCache::hashRemove(void *obj)
00259 {
00260   int index = hash(obj);
00261 
00262   // find element
00263   int curIndex = m_hash[index].head;
00264   int prevIndex=-1;
00265   while (m_hash[curIndex].obj!=obj)
00266   {
00267     prevIndex = curIndex;
00268     curIndex = m_hash[curIndex].nextHash;     
00269   }
00270 
00271   if (prevIndex==-1) // remove from start
00272   {
00273     m_hash[index].head = m_hash[curIndex].nextHash;
00274   }
00275   else // remove in the middle
00276   {
00277     m_hash[prevIndex].nextHash = m_hash[curIndex].nextHash;
00278   }
00279 
00280   // add curIndex element to empty list
00281   m_hash[curIndex].nextHash = m_freeHashNodes;
00282   m_hash[curIndex].index = -1;
00283   m_hash[curIndex].obj   = 0;
00284   m_freeHashNodes = curIndex;
00285 }
00286 
00287 #ifdef CACHE_TEST
00288 int main()
00289 {
00290   int i;
00291   struct obj
00292   {
00293     obj() : handle(-1) {}
00294     int handle;
00295   };
00296   obj *objs = new obj[100];
00297   ObjCache c(3);
00298   for (i=0;i<32;i++)
00299   {
00300     int objId=(i%3)+(i>>2)*4;
00301     printf("------- use(%d=%p)--------\n",objId,&objs[objId]);
00302 #ifdef CACHE_DEBUG
00303     c.printLRU();
00304 #endif
00305     obj *victim=0;
00306     if (objs[objId].handle==-1)
00307     {
00308       objs[objId].handle = c.add(&objs[objId],(void**)&victim);
00309       if (victim) victim->handle=-1;
00310     }
00311     else
00312     {
00313       c.use(objs[objId].handle);
00314     }
00315     printf("i=%d objId=%d using %p victim=%p\n",i,objId,&objs[objId],victim);
00316   }
00317   for (i=0;i<100;i++)
00318   {
00319     if (objs[i].handle!=-1)
00320     {
00321       printf("------ del objId=%d handle=%d ------\n",i,objs[i].handle);
00322       c.del(objs[i].handle);
00323       objs[i].handle=-1;
00324 #ifdef CACHE_DEBUG
00325       c.printLRU();
00326 #endif
00327     }
00328   }
00329   c.printStats();
00330   return 0;
00331 }
00332 #endif



Generated on Mon Mar 31 10:58:41 2008 by  doxygen 1.5.1