Aleph-w  1.5a.2
Biblioteca general de algoritmos y estructuras de datos
 Todo Clases Archivos Funciones Variables 'typedefs' Enumeraciones Amigas Grupos Páginas
tpl_cache.H
1 # ifndef TPL_CACHE_H
2 # define TPL_CACHE_H
3 
4 # include <memory>
5 # include <aleph.H>
6 # include <tpl_dnode.H>
7 # include <tpl_lhash.H>
8 
9 namespace Aleph {
10 
11 /*
12  Esta es una implantacion de un cache asociativo basado en una tabla
13  hash. El cache maneja pares <Key, Data> donde Key es la clave
14  asociativa y Data es el dato relacionado a Key. No se permiten
15  pares <Key, Data> duplicados pero si es posible tener key
16  duplicados.
17 
18  El cache tiene un tamaño definido por cache_size especificado en el
19  constructor. Cuando el número de pares insertados en el cache
20  alcanza cache_size, entonces se dice que el cache está lleno. Si se
21  intenta insertar un nuevo par en un cache lleno, entonces se debe
22  eliminar un par. En esta implatación, se elimina el par menos
23  recientemente utilizado (lru de sus siglas en inglés).
24 
25  La implantación se basa en una tabla hash con resolución de
26  colisiones por encadenamiento separado. Cada bucket de la tabla
27  almacena el par. Adicionalmente, el bucket contiene un enlace que
28  funge de conector a una lista doblemente enlazada que simula el
29  orden lru. Además, el bucket contiene un enlace directo al cache
30  usado para actualizar sus estadísticas.
31 
32  Los pares pueden ser "bloqueados"; esto es: cuando un par es
33  bloqueado, este no puede eliminarse del cache hasta que éste no sea
34  liberado. Un bucket bloqueado nunca será seleccionado para
35  reemplazo por la política lru.
36 
37 */
38 
39 
40  template <class Key, class Data>
41 class Cache
42 {
43 public:
44 
45  class Cache_Entry : public LhashTable<Key>::Bucket
46  {
47  friend class Cache<Key, Data>;
48 
49  Data data;
50  Dlink dlink_lru; // enlace cola lru
51  Dlink dlink_inside;
52 
53  bool locked; // Indicates that entry cannot be deleted
54 
55  bool is_in_hash_table;
56 
57  void lock() Exception_Prototypes(std::runtime_error)
58  {
59  if (locked)
60  throw std::runtime_error("Cache_Entry is already locked");
61 
62  locked = true;
63  }
64 
65  void unlock() Exception_Prototypes(std::runtime_error)
66  {
67  if (not locked)
68  throw std::runtime_error("Cache_Entry is not locked");
69 
70  locked = false;
71  }
72 
73  Dlink* link_lru() { return &dlink_lru; }
74 
75  Dlink* link_inside() { return &dlink_inside; }
76 
77  public:
78 
79  Cache_Entry(const Key & k, const Data & d) :
81  data(d), locked(false), is_in_hash_table(false)
82  {
83  // empty
84  }
85 
86  Cache_Entry() : locked(false), is_in_hash_table(false)
87  {
88  // empty
89  }
90 
91  Data & get_data() { return data; }
92 
93  const bool & is_locked() const { return locked; }
94 
95  const bool & is_in_table() const { return is_in_hash_table; }
96 
97  static Cache_Entry * convert_to_cache_entry(Data * data_ptr)
98  {
99  return reinterpret_cast<Cache_Entry*>(data_ptr);
100  }
101  }; // fin class Cache_Entry
102 
103 
104 private:
105 
106  Dlink lru_list;
107  Dlink locked_list;
108  Dlink inside_list;
109 
110  LhashTable<Key> hash_table;
111 
112  size_t cache_size; /* longitud del cache */
113 
114 public:
115 
116  const size_t & get_num_entries() const
117  {
118  I(hash_table.size() <= cache_size);
119 
120  return hash_table.size();
121  }
122 
123 private:
124 
125  size_t num_lru; /* numero de elementos en lista lru */
126  size_t num_locked; /* numero de elementos bloqueados */
127 
128  /* This is a list of chunks of memory. Each one is added to the list
129  while requiered during the cache lifetime.
130 
131  A chunk of memory has the interesting property of being continuous
132  in memory, therefore can be cached by the computer.
133  */
134  typedef Dnode<Cache_Entry*> Chunk_Descriptor;
135 
136  Chunk_Descriptor chunk_list;
137 
138 protected:
139 
140  LINKNAME_TO_TYPE(Cache_Entry, dlink_lru);
141 
142  LINKNAME_TO_TYPE(Cache_Entry, dlink_inside);
143 
144  void insert_entry_to_lru_list(Cache_Entry * cache_entry)
145  {
146  num_lru++;
147  lru_list.insert(cache_entry->link_lru());
148  }
149 
150  void remove_entry_from_lru_list(Cache_Entry * cache_entry)
151  {
152  num_lru--;
153  cache_entry->link_lru()->del();
154  }
155 
156  void insert_entry_to_locked_list(Cache_Entry * cache_entry)
157  {
158  num_locked++;
159  locked_list.insert(cache_entry->link_lru());
160  }
161 
162  void remove_entry_from_locked_list(Cache_Entry * cache_entry)
163  {
164  num_locked--;
165  cache_entry->link_lru()->del();
166  }
167 
168  void move_to_inside_front(Cache_Entry * cache_entry)
169  {
170  cache_entry->link_inside()->del();
171  inside_list.insert(cache_entry->link_inside());
172  }
173 
174  void move_to_lru_front(Cache_Entry * cache_entry)
175  {
176  cache_entry->link_lru()->del();
177  lru_list.insert(cache_entry->link_lru());
178  }
179 
180  void move_to_lru_rear(Cache_Entry * cache_entry)
181  {
182  cache_entry->link_lru()->del();
183  lru_list.append(cache_entry->link_lru());
184  }
185 
186  void do_mru(Cache_Entry * cache_entry)
187  {
188  move_to_lru_front(cache_entry);
189  }
190 
191  void do_lru(Cache_Entry * cache_entry)
192  {
193  move_to_lru_rear(cache_entry);
194  }
195 
196  /*
197  elimina de tabla hash y hace la entrada la menos recientemente usada
198  */
199  void remove_entry_from_hash_table(Cache_Entry * cache_entry)
200  {
201  I(not cache_entry->is_locked());
202 
203  cache_entry->link_inside()->del();
204 
205  hash_table.remove(cache_entry);
206  cache_entry->is_in_hash_table = false;
207  do_lru(cache_entry);
208  }
209 
210  /*
211  busca y retorna proxima entrada segun prioridad lru que no tenga
212  cerrojo (lock), elimina de la tabla hash y hace la entrada la mas
213  recientemente usada.
214  */
215  Cache_Entry * get_lru_entry()
216  {
217  I(hash_table.size() <= cache_size);
218 
219  if (lru_list.is_empty())
220  throw std::bad_alloc ();
221 
222  Dlink * lru_entry_link = lru_list.get_prev();
223  Cache_Entry * cache_entry = dlink_lru_to_Cache_Entry(lru_entry_link);
224 
225  I(not cache_entry->is_locked());
226 
227  if (cache_entry->is_in_hash_table)
228  {
229  I(hash_table.search(cache_entry->get_key()) == cache_entry);
230  remove_entry_from_hash_table(cache_entry);
231  }
232 
233  do_mru(cache_entry);
234 
235  return cache_entry;
236  }
237 
238  Cache_Entry * insert_pair(const Key & key, const Data & data)
239  {
240  Cache_Entry *cache_entry = get_lru_entry();
241 
242  cache_entry->get_key() = key;
243  cache_entry->get_data() = data;
244 
245  inside_list.insert(cache_entry->link_inside());
246 
247  hash_table.insert(cache_entry);
248  cache_entry->is_in_hash_table = true;
249 
250  return cache_entry;
251  }
252 
253 public:
254 
255  Cache(size_t (*hash_fct)(const Key&), const size_t & size) :
256  hash_table(hash_fct, size, false),
257  cache_size(hash_table.capacity()), num_lru(0), num_locked(0)
258  {
259  I(size > 1);
260  I(hash_fct != NULL);
261 
262  Cache_Entry * entries_array = new Cache_Entry [cache_size];
263 
264  try
265  {
266  std::unique_ptr<Chunk_Descriptor>
267  chunk_descriptor (new Chunk_Descriptor (entries_array));
268 
269  chunk_list.insert(chunk_descriptor.get());
270 
271  for (int i = 0; i < cache_size; i++)
272  insert_entry_to_lru_list(&entries_array[i]);
273 
274  chunk_descriptor.release();
275  }
276  catch (...)
277  {
278  delete [] entries_array;
279  throw;
280  }
281  }
282 
283  virtual ~Cache()
284  {
285  I(hash_table.size() <= cache_size);
286 
287  while (not chunk_list.is_empty())
288  {
289  Chunk_Descriptor * current_chunk = chunk_list.remove_next();
290 
291  delete [] current_chunk->get_data();
292  delete current_chunk;
293  }
294  }
295 
296  Cache_Entry * search(const Key & key)
297  {
298  I(hash_table.size() <= cache_size);
299 
300  Cache_Entry * cache_entry =
301  static_cast<Cache_Entry*>(hash_table.search(key));
302 
303  if (cache_entry != NULL)
304  {
305  do_mru(cache_entry);
306  move_to_inside_front(cache_entry);
307  }
308 
309  return cache_entry;
310  }
311 
312  /* retorna proxima entrada del cache con la misma clave de
313  cache_entry si esta existe o nulo de lo contrario */
314  Cache_Entry * search_next(Cache_Entry * cache_entry)
315  {
316  Cache_Entry *next_entry =
317  static_cast<Cache_Entry*>(hash_table.search_next(cache_entry));
318 
319  if (next_entry != NULL)
320  {
321  do_mru(next_entry);
322  move_to_inside_front(cache_entry);
323  }
324 
325  return next_entry;
326  }
327 
328  Cache_Entry * insert(const Key& key, const Data& data)
329  Exception_Prototypes (std::bad_alloc)
330  {
331  I(hash_table.size() <= cache_size);
332 
333  return insert_pair(key, data);
334  }
335 
336  void lock_entry(Cache_Entry * cache_entry)
337  Exception_Prototypes(std::runtime_error)
338  {
339  I(num_locked < get_num_entries());
340  I(num_lru > 0);
341  I(hash_table.search(cache_entry->get_key()) == cache_entry);
342  I(cache_entry->is_in_hash_table);
343 
344  if (cache_entry->is_locked())
345  throw std::runtime_error("Cache_Entry is already locked");
346 
347  remove_entry_from_lru_list(cache_entry);
348  insert_entry_to_locked_list(cache_entry);
349 
350  cache_entry->lock();
351  }
352 
353  void unlock_entry(Cache_Entry * cache_entry)
354  Exception_Prototypes(std::runtime_error)
355  {
356  I(hash_table.search(cache_entry->get_key()) == cache_entry);
357  I(cache_entry->is_in_hash_table);
358  I(num_locked <= get_num_entries());
359 
360  if (not cache_entry->is_locked())
361  throw std::runtime_error("Cache_Entry is not locked");
362 
363  remove_entry_from_locked_list(cache_entry);
364  insert_entry_to_lru_list(cache_entry);
365 
366  cache_entry->unlock();
367  }
368 
369  void remove(Cache_Entry * cache_entry)
370  Exception_Prototypes(std::runtime_error)
371  {
372  I(hash_table.search(cache_entry->get_key()) == cache_entry);
373 
374  if (cache_entry->is_locked())
375  throw std::runtime_error("Cache_Entry is already locked");
376 
377  remove_entry_from_hash_table(cache_entry);
378  }
379 
380  void expand(const size_t & plus_size)
381  Exception_Prototypes(std::range_error, std::bad_alloc)
382  {
383  I(hash_table.size() <= cache_size);
384 
385  if (plus_size == 0)
386  throw std::range_error ("bad plus_size");
387 
388  const size_t new_cache_size = cache_size + plus_size;
389 
390  Cache_Entry * entries_array = new Cache_Entry [plus_size];
391 
392  try
393  {
394  std::unique_ptr<Chunk_Descriptor>
395  chunk_descriptor (new Chunk_Descriptor (entries_array));
396 
397  ID(long old_num_items = hash_table.size());
398 
399  hash_table.resize(13*(new_cache_size)/10);
400 
401  I(old_num_items == hash_table.size());
402 
403  for (int i = 0; i < plus_size; i++)
404  insert_entry_to_lru_list(&entries_array[i]);
405 
406  chunk_list.insert(chunk_descriptor.release());
407 
408  cache_size = new_cache_size;
409  }
410  catch (...)
411  {
412  delete [] entries_array;
413  throw;
414  }
415  }
416 
417  const size_t & capacity() const { return cache_size; }
418 
419  const size_t & size() const { return hash_table.size(); }
420 
421  const size_t & get_num_locked() const { return num_locked; }
422 
423  const size_t & get_num_busy_slots() const
424  {
425  return hash_table.get_num_busy_slots();
426  }
427 
428  struct Iterator : public Dlink::Iterator
429  {
430  Iterator(Cache& _cache) : Dlink::Iterator(&_cache.inside_list)
431  {
432  // empty
433  }
434 
435  Cache_Entry * get_current()
436  {
437  Cache_Entry * ret_val =
438  Cache_Entry::dlink_inside_to_Cache_Entry(Dlink::Iterator::get_current());
439 
440  I(ret_val->is_in_table());
441 
442  return ret_val;
443  }
444  };
445 };
446 
447 } // end namespace Aleph
448 
449 # endif // TPL_CACHE_H
const size_t & size() const
Retorna el número de elementos que contiene la tabla.
Definition: tpl_lhash.H:286
Bucket * insert(Bucket *bucket)
Definition: tpl_lhash.H:160
#define LINKNAME_TO_TYPE(type_name, link_name)
Definition: dlink.H:741
Definition: tpl_cache.H:428
Dnode< T > * remove_next()
Elimina sucesor a this y retorna su dirección.
Definition: tpl_dnode.H:37
Itor1 search(Itor1 beg, const Itor1 &end, Itor2 searchBeg, const Itor2 &searchEnd, BinaryPredicate op=BinaryPredicate())
Definition: ahAlgo.H:327
Definition: tpl_cache.H:45
Bucket * search(const Key &key) const
Definition: tpl_lhash.H:182
Definition: tpl_lhash.H:531
Bucket * search_next(Bucket *bucket) const
Definition: tpl_lhash.H:259
size_t resize(size_t new_size)
Definition: tpl_lhash.H:222
const size_t & get_num_busy_slots() const
Retorna la cantidad de entradas del arreglo que están ocupadas.
Definition: tpl_lhash.H:289
Bucket * remove(Bucket *bucket)
Definition: tpl_lhash.H:209
Definition: tpl_cache.H:41
T & get_data()
Retorna referencia a dato contenido en el nodo.
Definition: tpl_dnode.H:126

Leandro Rabindranath León