objcache.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  *
3  *
4  *
5  * Copyright (C) 1997-2015 by Dimitri van Heesch.
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation under the terms of the GNU General Public License is hereby
9  * granted. No representations are made about the suitability of this software
10  * for any purpose. It is provided "as is" without express or implied warranty.
11  * See the GNU General Public License for more details.
12  *
13  * Documents produced by Doxygen are derivative works derived from the
14  * input used in their production; they are not affected by this license.
15  *
16  */
17 
18 #include <stdio.h>
19 #include <assert.h>
20 #include <qglobal.h>
21 #include "objcache.h"
22 #if !defined(_OS_WIN32_) || defined(__MINGW32__)
23 #include <stdint.h>
24 #endif
25 
26 //----------------------------------------------------------------------
27 
28 ObjCache::ObjCache(unsigned int logSize)
29  : m_head(-1), m_tail(-1), //m_numEntries(0),
30  m_size(1<<logSize), m_count(0), m_freeHashNodes(0), m_freeCacheNodes(0),
31  m_lastHandle(-1)
32 {
33  int i;
34  m_cache = new CacheNode[m_size];
35  m_hash = new HashNode[m_size];
36  // add all items to list of free buckets
37  for (i=0;i<m_size-1;i++)
38  {
39  m_hash[i].nextHash = i+1;
40  m_cache[i].next = i+1;
41  }
42  m_misses = 0;
43  m_hits = 0;
44 }
45 
47 {
48  delete[] m_cache;
49  delete[] m_hash;
50 }
51 
52 int ObjCache::add(void *obj,void **victim)
53 {
54  *victim=0;
55 
56  HashNode *hnode = hashFind(obj);
57  //printf("hnode=%p\n",hnode);
58  if (hnode) // move object to the front of the LRU list, since it is used
59  // most recently
60  {
61  //printf("moveToFront=%d\n",hnode->index);
62  moveToFront(hnode->index);
63  m_hits++;
64  }
65  else // object not in the cache.
66  {
67  void *lruObj=0;
68  if (m_freeCacheNodes!=-1) // cache not full -> add element to the cache
69  {
70  // remove element from free list
71  int index = m_freeCacheNodes;
73 
74  // add to head of the list
75  if (m_tail==-1)
76  {
77  m_tail = index;
78  }
79  m_cache[index].prev = -1;
81  if (m_head!=-1)
82  {
84  }
85  m_head = index;
86  m_count++;
87  }
88  else // cache full -> replace element in the cache
89  {
90  //printf("Cache full!\n");
91  lruObj = m_cache[m_tail].obj;
92  hashRemove(lruObj);
93  moveToFront(m_tail); // m_tail indexes the emptied element, which becomes m_head
94  }
95  //printf("numEntries=%d size=%d\n",m_numEntries,m_size);
96  m_cache[m_head].obj = obj;
97  hnode = hashInsert(obj);
98  hnode->index = m_head;
99  *victim = lruObj;
100  m_misses++;
101  }
102  return m_head;
103 }
104 
106 {
107  assert(index!=-1);
108  assert(m_cache[index].obj!=0);
109  hashRemove(m_cache[index].obj);
110  moveToFront(index);
112  if (m_head==-1)
113  m_tail=-1;
114  else
115  m_cache[m_head].prev=-1;
116  m_cache[index].obj=0;
117  m_cache[index].prev=-1;
120  m_count--;
121 }
122 
123 #ifdef CACHE_DEBUG
124 #define cache_debug_printf printf
125 void ObjCache::printLRU()
126 {
127  cache_debug_printf("MRU->LRU: ");
128  int index = m_head;
129  while (index!=-1)
130  {
131  cache_debug_printf("%d=%p ",index,m_cache[index].obj);
132  index = m_cache[index].next;
133  }
134  cache_debug_printf("\n");
135 
136  cache_debug_printf("LRU->MRU: ");
137  index = m_tail;
138  while (index!=-1)
139  {
140  cache_debug_printf("%d=%p ",index,m_cache[index].obj);
141  index = m_cache[index].prev;
142  }
143  cache_debug_printf("\n");
144 }
145 #endif
146 
147 #ifdef CACHE_STATS
148 #define cache_stats_printf printf
150 {
151  cache_stats_printf("ObjCache: hits=%d misses=%d hit ratio=%f\n",m_hits,m_misses,m_hits*100.0/(m_hits+m_misses));
152 }
153 #endif
154 
156 {
157  int prev,next;
158  if (m_head!=index)
159  {
160  next = m_cache[index].next;
161  prev = m_cache[index].prev;
162 
163  // de-chain node at index
164  m_cache[prev].next = next;
165  if (next!=-1) m_cache[next].prev = prev; else m_tail = prev;
166 
167  // add to head
168  m_cache[index].prev = -1;
171  m_head = index;
172  }
173 }
174 
175 unsigned int ObjCache::hash(void *addr)
176 {
177  static bool isPtr64 = sizeof(addr)==8;
178  if (isPtr64)
179  {
180  uint64 key = (uint64)addr;
181  // Thomas Wang's 64 bit Mix Function
182  key += ~(key << 32);
183  key ^= (key >> 22);
184  key += ~(key << 13);
185  key ^= (key >> 8);
186  key += (key << 3);
187  key ^= (key >> 15);
188  key += ~(key << 27);
189  key ^= (key >> 31);
190  return (unsigned int)(key & (m_size-1));
191  }
192  else
193  {
194  // Thomas Wang's 32 bit Mix Function
195  uintptr_t key = (uintptr_t)addr;
196  key += ~(key << 15);
197  key ^= (key >> 10);
198  key += (key << 3);
199  key ^= (key >> 6);
200  key += ~(key << 11);
201  key ^= (key >> 16);
202  return (unsigned int)(key & (m_size-1));
203  }
204 }
205 
207 {
208  HashNode *node = 0;
209  int index = m_hash[hash(obj)].head;
210  //printf("hashFind: obj=%p index=%d\n",obj,index);
211  while (index!=-1 &&
212  m_hash[index].obj!=obj
213  ) // search for right object in the list
214  {
215  index = m_hash[index].nextHash;
216  }
217  // found the obj at index, so it is in the cache!
218  if (index!=-1)
219  {
220  node = &m_hash[index];
221  }
222  return node;
223 }
224 
226 {
227  int index = hash(obj);
228  //printf("Inserting %p index=%d\n",obj,index);
229 
230  // remove element from empty list
231  int newElement = m_freeHashNodes;
232  assert(newElement!=-1);
234 
235  if (m_hash[index].head!=-1) // hash collision -> goto end of the list
236  {
237  index = m_hash[index].head;
238  while (m_hash[index].nextHash!=-1)
239  {
240  index = m_hash[index].nextHash;
241  }
242  // add to end of the list
243  m_hash[index].nextHash = newElement;
244  }
245  else // first element in the hash list
246  {
247  m_hash[index].head = newElement;
248  }
249  // add to the end of the list
250  m_hash[newElement].nextHash = -1;
251  m_hash[newElement].obj = obj;
252  return &m_hash[newElement];
253 }
254 
255 void ObjCache::hashRemove(void *obj)
256 {
257  int index = hash(obj);
258 
259  // find element
260  int curIndex = m_hash[index].head;
261  int prevIndex=-1;
262  while (m_hash[curIndex].obj!=obj)
263  {
264  prevIndex = curIndex;
265  curIndex = m_hash[curIndex].nextHash;
266  }
267 
268  if (prevIndex==-1) // remove from start
269  {
270  m_hash[index].head = m_hash[curIndex].nextHash;
271  }
272  else // remove in the middle
273  {
274  m_hash[prevIndex].nextHash = m_hash[curIndex].nextHash;
275  }
276 
277  // add curIndex element to empty list
278  m_hash[curIndex].nextHash = m_freeHashNodes;
279  m_hash[curIndex].index = -1;
280  m_hash[curIndex].obj = 0;
281  m_freeHashNodes = curIndex;
282 }
283 
284 #ifdef CACHE_TEST
285 int main()
286 {
287  int i;
288  struct obj
289  {
290  obj() : handle(-1) {}
291  int handle;
292  };
293  obj *objs = new obj[100];
294  ObjCache c(3);
295  for (i=0;i<32;i++)
296  {
297  int objId=(i%3)+(i>>2)*4;
298  printf("------- use(%d=%p)--------\n",objId,&objs[objId]);
299 #ifdef CACHE_DEBUG
300  c.printLRU();
301 #endif
302  obj *victim=0;
303  if (objs[objId].handle==-1)
304  {
305  objs[objId].handle = c.add(&objs[objId],(void**)&victim);
306  if (victim) victim->handle=-1;
307  }
308  else
309  {
310  c.use(objs[objId].handle);
311  }
312  printf("i=%d objId=%d using %p victim=%p\n",i,objId,&objs[objId],victim);
313  }
314  for (i=0;i<100;i++)
315  {
316  if (objs[i].handle!=-1)
317  {
318  printf("------ del objId=%d handle=%d ------\n",i,objs[i].handle);
319  c.del(objs[i].handle);
320  objs[i].handle=-1;
321 #ifdef CACHE_DEBUG
322  c.printLRU();
323 #endif
324  }
325  }
326  c.printStats();
327  return 0;
328 }
329 #endif
Cache for objects.
Definition: objcache.h:33
void use(int handle)
Definition: objcache.h:72
#define cache_stats_printf
Definition: objcache.cpp:148
int m_head
Definition: objcache.h:115
unsigned int hash(void *addr)
Definition: objcache.cpp:175
int m_tail
Definition: objcache.h:116
void hashRemove(void *obj)
Definition: objcache.cpp:255
void del(int handle)
Definition: objcache.cpp:105
CacheNode * m_cache
Definition: objcache.h:113
int m_count
Definition: objcache.h:118
void printLRU()
int m_freeHashNodes
Definition: objcache.h:119
HashNode * m_hash
Definition: objcache.h:114
int add(void *obj, void **victim)
Definition: objcache.cpp:52
void moveToFront(int index)
Definition: objcache.cpp:155
def key(type, name=None)
Definition: graph.py:13
int m_misses
Definition: objcache.h:122
void printStats()
Definition: objcache.cpp:149
int m_size
Definition: objcache.h:117
~ObjCache()
Definition: objcache.cpp:46
int m_hits
Definition: objcache.h:123
unsigned long long uint64
Definition: qglobal.h:361
HashNode * hashInsert(void *obj)
Definition: objcache.cpp:225
ObjCache(unsigned int logSize)
Definition: objcache.cpp:28
int m_freeCacheNodes
Definition: objcache.h:120
constexpr std::enable_if_t< are_cv_compatible< TO, FROM >::value, std::add_pointer_t< std::remove_pointer_t< TO > > > addr(FROM &from)
Definition: ensurePointer.h:35
HashNode * hashFind(void *obj)
Definition: objcache.cpp:206