An open source method of characteristics neutron transport code.
ParallelHashMap.h
Go to the documentation of this file.
1 
11 #ifndef __PARALLEL_HASH_MAP__
12 #define __PARALLEL_HASH_MAP__
13 #include<iostream>
14 #include<stdexcept>
15 #include<functional>
16 #include<omp.h>
17 
18 #include "log.h"
19 
20 
32 template <class K, class V>
33 class FixedHashMap {
34  struct node {
35  node(K k_in, V v_in) : next(NULL), key(k_in), value(v_in) {}
36  K key;
37  V value;
38  node *next;
39  };
40 
41  private:
42  size_t _M; /* table size */
43  size_t _N; /* number of elements present in table */
44  node ** _buckets; /* buckets of values stored in nodes */
45 
46  public:
47 
48  FixedHashMap(size_t M = 64);
49  virtual ~FixedHashMap();
50  bool contains(K& key);
51  V& at(K& key);
52  void insert(K key, V value);
53  long insert_and_get_count(K key, V value);
54  size_t size();
55  size_t bucket_count();
56  K* keys();
57  V* values();
58  void clear();
59  void print_buckets();
60 };
61 
62 
75 template <class K, class V>
77 
78  /* padded pointer to hash table to avoid false sharing */
79  struct paddedPointer {
80  volatile long pad_L1;
81  volatile long pad_L2;
82  volatile long pad_L3;
83  volatile long pad_L4;
84  volatile long pad_L5;
85  volatile long pad_L6;
86  volatile long pad_L7;
87  FixedHashMap<K,V> volatile* value;
88  volatile long pad_R1;
89  volatile long pad_R2;
90  volatile long pad_R3;
91  volatile long pad_R4;
92  volatile long pad_R5;
93  volatile long pad_R6;
94  volatile long pad_R7;
95  volatile long pad_R8;
96  };
97 
98  private:
99  FixedHashMap<K,V> *_table;
100  paddedPointer *_announce;
101  size_t _num_threads;
102  size_t _N;
103  omp_lock_t * _locks;
104  size_t _num_locks;
105  bool _fixed_size;
106  void resize();
107 
108  public:
109  ParallelHashMap(size_t M = 64, size_t L = 64);
110  virtual ~ParallelHashMap();
111  bool contains(K& key);
112  V at(K& key);
113  void update(K& key, V value);
114  void insert(K key, V value);
115  long insert_and_get_count(K key, V value);
116  size_t size();
117  size_t bucket_count();
118  size_t num_locks();
119  K* keys();
120  V* values();
121  void clear();
122  void setFixedSize();
123  void print_buckets();
124  void realloc(size_t M);
125 };
126 
127 
137 template <class K, class V>
139 
140  /* ensure M is a power of 2 */
141  if ((M & (M-1)) != 0) {
142  /* if not, round up to nearest power of 2 */
143  M--;
144  for (size_t i = 1; i < 8 * sizeof(size_t); i*=2)
145  M |= M >> i;
146  M++;
147  }
148 
149  /* allocate table */
150  _M = M;
151  _N = 0;
152  _buckets = new node*[_M]();
153 }
154 
155 
160 template <class K, class V>
162  /* for each bucket, scan through linked list and delete all nodes */
163  for (size_t i=0; i<_M; i++) {
164  node *iter_node = _buckets[i];
165  while (iter_node != NULL) {
166  node *next_node = iter_node->next;
167  delete iter_node;
168  iter_node = next_node;
169  }
170  }
171 
172  /* delete all buckets (now pointers to empty linked lists) */
173  delete [] _buckets;
174 }
175 
176 
184 template <class K, class V>
186 
187  /* get hash into table assuming M is a power of 2, using fast modulus */
188  size_t key_hash = std::hash<K>()(key) & (_M-1);
189 
190  /* search corresponding bucket for key */
191  node *iter_node = _buckets[key_hash];
192  while (iter_node != NULL) {
193  if (iter_node->key == key)
194  return true;
195  else
196  iter_node = iter_node->next;
197  }
198  return false;
199 }
200 
201 
211 template <class K, class V>
213 
214  /* get hash into table assuming M is a power of 2, using fast modulus */
215  size_t key_hash = std::hash<K>()(key) & (_M-1);
216 
217  /* search bucket for key and return the corresponding value if found */
218  node *iter_node = _buckets[key_hash];
219  while (iter_node != NULL) {
220  if (iter_node->key == key)
221  return iter_node->value;
222  else
223  iter_node = iter_node->next;
224  }
225 
226  /* after the bucket has been completely searched without finding the key,
227  print an error message */
228  log_printf(ERROR, "Key not present in map");
229 }
230 
231 
240 template <class K, class V>
241 void FixedHashMap<K,V>::insert(K key, V value) {
242 
243  /* get hash into table using fast modulus */
244  size_t key_hash = std::hash<K>()(key) & (_M-1);
245 
246  /* check to see if key already exists in map */
247  if (contains(key))
248  return;
249 
250  /* create new node */
251  node *new_node = new node(key, value);
252 
253  /* find where to place element in linked list */
254  node **iter_node = &_buckets[key_hash];
255  while (*iter_node != NULL)
256  iter_node = &(*iter_node)->next;
257 
258  /* place element in linked list */
259  *iter_node = new_node;
260 
261  /* increment counter */
262 #pragma omp atomic
263  _N++;
264 }
265 
266 
278 template <class K, class V>
280 
281  /* get hash into table using fast modulus */
282  size_t key_hash = std::hash<K>()(key) & (_M-1);
283 
284  /* check to see if key already exists in map */
285  if (contains(key))
286  return -1;
287 
288  /* create new node */
289  node *new_node = new node(key, value);
290 
291  /* find where to place element in linked list */
292  node **iter_node = &_buckets[key_hash];
293  while (*iter_node != NULL)
294  iter_node = &(*iter_node)->next;
295 
296  /* place element in linked list */
297  *iter_node = new_node;
298 
299  /* increment counter and return number */
300  size_t N;
301 #pragma omp atomic capture
302  N = _N++;
303 
304  return (long) N;
305 }
306 
307 
312 template <class K, class V>
314  return _N;
315 }
316 
317 
322 template <class K, class V>
324  return _M;
325 }
326 
327 
337 template <class K, class V>
339 
340  /* allocate array of keys */
341  K *key_list = new K[_N];
342 
343  /* fill array with keys */
344  size_t ind = 0;
345  for (size_t i=0; i<_M; i++) {
346  node *iter_node = _buckets[i];
347  while (iter_node != NULL) {
348  key_list[ind] = iter_node->key;
349  iter_node = iter_node->next;
350  ind++;
351  }
352  }
353  return key_list;
354 }
355 
356 
366 template <class K, class V>
368 
369  /* allocate array of values */
370  V *values = new V[_N];
371 
372  /* fill array with values */
373  size_t ind = 0;
374  for (size_t i=0; i<_M; i++) {
375  node *iter_node = _buckets[i];
376  while (iter_node != NULL) {
377  values[ind] = iter_node->value;
378  iter_node = iter_node->next;
379  ind++;
380  }
381  }
382  return values;
383 }
384 
385 
389 template <class K, class V>
391 
392  /* for each bucket, scan through linked list and delete all nodes */
393  for (size_t i=0; i<_M; i++) {
394  node *iter_node = _buckets[i];
395  while (iter_node != NULL) {
396  node *next_node = iter_node->next;
397  delete iter_node;
398  iter_node = next_node;
399  }
400  }
401 
402  /* reset each bucket to null */
403  for (size_t i=0; i<_M; i++)
404  _buckets[i] = NULL;
405 
406  /* reset the number of entries to zero */
407  _N = 0;
408 }
409 
410 
418 template <class K, class V>
420  log_printf(NORMAL, "Printing all buckets in the hash map...");
421  for (size_t i=0; i<_M; i++) {
422  if (_buckets[i] == NULL)
423  log_printf(NORMAL, "Bucket %d -> NULL", i);
424  else
425  log_printf(NORMAL, "Bucket %d -> %p", i, _buckets[i]);
426  }
427 }
428 
429 
434 template <class K, class V>
436 
437  /* allocate table */
438  _table = new FixedHashMap<K,V>(M);
439  _fixed_size = false;
440 
441  /* get number of threads and create concurrency structures */
442  _num_threads = 1;
443  _num_threads = omp_get_max_threads();
444  _num_locks = L;
445  _locks = new omp_lock_t[_num_locks];
446  for (size_t i=0; i<_num_locks; i++)
447  omp_init_lock(&_locks[i]);
448 
449  _announce = new paddedPointer[_num_threads];
450  for (size_t t=0; t<_num_threads; t++)
451  _announce[t].value = NULL;
452 }
453 
454 
459 template <class K, class V>
461  delete _table;
462  delete [] _locks;
463  delete [] _announce;
464 }
465 
466 
479 template <class K, class V>
481 
482  /* get thread ID */
483  size_t tid = 0;
484  tid = omp_get_thread_num();
485 
486  /* get pointer to table, announce it will be searched,
487  and ensure consistency */
488  FixedHashMap<K,V> *table_ptr;
489  do {
490  table_ptr = _table;
491  _announce[tid].value = table_ptr;
492 #pragma omp flush
493  } while (table_ptr != _table);
494 
495  /* see if current table contains the thread */
496  bool present = table_ptr->contains(key);
497 
498  /* reset table announcement to not searching */
499  _announce[tid].value = NULL;
500 
501  return present;
502 }
503 
504 
517 template <class K, class V>
519 
520  /* If the size is fixed, simply return the value from the fixed hash map */
521  if (_fixed_size)
522  return _table->at(key);
523 
524  /* get thread ID */
525  size_t tid = 0;
526  tid = omp_get_thread_num();
527 
528  /* get pointer to table, announce it will be searched */
529  FixedHashMap<K,V> *table_ptr;
530  do {
531  table_ptr = _table;
532  _announce[tid].value = table_ptr;
533 #pragma omp flush
534  } while (table_ptr != _table);
535 
536  /* get value associated with the key in the underlying table */
537  V value = table_ptr->at(key);
538 
539  /* reset table announcement to not searching */
540  _announce[tid].value = NULL;
541 
542  return value;
543 }
544 
545 
556 template <class K, class V>
557 void ParallelHashMap<K,V>::insert(K key, V value) {
558  /* check if resize needed */
559  if (2*_table->size() > _table->bucket_count())
560  resize();
561 
562  /* check to see if key is already contained in the table */
563  if (contains(key))
564  return;
565 
566  /* get lock hash */
567  size_t lock_hash = (std::hash<K>()(key) & (_table->bucket_count() - 1))
568  % _num_locks;
569 
570  /* acquire lock */
571  omp_set_lock(&_locks[lock_hash]);
572 
573  /* insert value */
574  _table->insert(key, value);
575 
576  /* release lock */
577  omp_unset_lock(&_locks[lock_hash]);
578 }
579 
580 
590 template <class K, class V>
591 void ParallelHashMap<K,V>::update(K& key, V value) {
592 
593  /* get lock hash */
594  size_t lock_hash = (std::hash<K>()(key) & (_table->bucket_count() - 1))
595  % _num_locks;
596 
597  /* acquire lock */
598  omp_set_lock(&_locks[lock_hash]);
599 
600  /* insert value */
601  _table->at(key) = value;
602 
603  /* release lock */
604  omp_unset_lock(&_locks[lock_hash]);
605 }
606 
607 
621 template <class K, class V>
623 
624  /* check if resize needed */
625  if (2*_table->size() > _table->bucket_count())
626  resize();
627 
628  /* check to see if key is already contained in the table */
629  if (contains(key))
630  return -1;
631 
632  /* get lock hash */
633  size_t lock_hash = (std::hash<K>()(key) & (_table->bucket_count() - 1))
634  % _num_locks;
635 
636  /* acquire lock */
637  omp_set_lock(&_locks[lock_hash]);
638 
639  /* insert value */
640  long N =_table->insert_and_get_count(key, value);
641 
642  /* release lock */
643  omp_unset_lock(&_locks[lock_hash]);
644 
645  return N;
646 }
647 
648 
661 template <class K, class V>
663 
664  /* do not resize if fixed size */
665  if (_fixed_size)
666  return;
667 
668  /* acquire all locks in order */
669  for (size_t i=0; i<_num_locks; i++)
670  omp_set_lock(&_locks[i]);
671 
672  /* recheck if resize needed */
673  if (2*_table->size() < _table->bucket_count()) {
674  /* release locks */
675  for (size_t i=0; i<_num_locks; i++)
676  omp_unset_lock(&_locks[i]);
677 
678  return;
679  }
680 
681  /* allocate new hash map of double the size */
682  FixedHashMap<K,V> *new_map =
683  new FixedHashMap<K,V>(2*_table->bucket_count());
684 
685  /* get keys, values, and number of elements */
686  K *key_list = _table->keys();
687  V *value_list = _table->values();
688 
689  /* insert key/value pairs into new hash map */
690  for (size_t i=0; i<_table->size(); i++)
691  new_map->insert(key_list[i], value_list[i]);
692 
693  /* save pointer of old table */
694  FixedHashMap<K,V> *old_table = _table;
695 
696  /* reassign pointer */
697  _table = new_map;
698 #pragma omp flush
699 
700  /* release all locks */
701  for (size_t i=0; i<_num_locks; i++)
702  omp_unset_lock(&_locks[i]);
703 
704  /* delete key and value list */
705  delete [] key_list;
706  delete [] value_list;
707 
708  /* wait for all threads to stop reading from the old table */
709  for (size_t i=0; i<_num_threads; i++) {
710  while (_announce[i].value == old_table) {
711 #pragma omp flush
712  continue;
713  }
714  }
715 
716  /* free memory associated with old table */
717  delete old_table;
718 }
719 
720 
725 template <class K, class V>
727  return _table->size();
728 }
729 
730 
735 template <class K, class V>
737  return _table->bucket_count();
738 }
739 
740 
745 template <class K, class V>
747  return _num_locks;
748 }
749 
750 
761 template <class K, class V>
763 
764  /* get thread ID */
765  size_t tid = 0;
766  tid = omp_get_thread_num();
767 
768  /* get pointer to table, announce it will be searched */
769  FixedHashMap<K,V> *table_ptr;
770  do {
771  table_ptr = _table;
772  _announce[tid].value = table_ptr;
773 #pragma omp flush
774  } while (table_ptr != _table);
775 
776  /* get key list */
777  K* key_list = table_ptr->keys();
778 
779  /* reset table announcement to not searching */
780  _announce[tid].value = NULL;
781 
782  return key_list;
783 }
784 
785 
796 template <class K, class V>
798 
799  /* get thread ID */
800  size_t tid = 0;
801  tid = omp_get_thread_num();
802 
803  /* get pointer to table, announce it will be searched */
804  FixedHashMap<K,V> *table_ptr;
805  do {
806  table_ptr = _table;
807  _announce[tid].value = table_ptr;
808 #pragma omp flush
809  } while (table_ptr != _table);
810 
811  /* get value list */
812  V* value_list = table_ptr->values();
813 
814  /* reset table announcement to not searching */
815  _announce[tid].value = NULL;
816 
817  return value_list;
818 }
819 
820 
824 template <class K, class V>
826  _fixed_size = true;
827 }
828 
829 
833 template <class K, class V>
835 
836  /* acquire all locks in order */
837  for (size_t i=0; i<_num_locks; i++)
838  omp_set_lock(&_locks[i]);
839 
840  /* clear underlying fixed table */
841  _table->clear();
842 
843  /* release all locks in order */
844  for (size_t i=0; i<_num_locks; i++)
845  omp_unset_lock(&_locks[i]);
846 }
847 
848 
857 template <class K, class V>
859 
860  /* get thread ID */
861  size_t tid = 0;
862  tid = omp_get_thread_num();
863 
864  /* get pointer to table, announce it will be searched */
865  FixedHashMap<K,V> *table_ptr;
866  do {
867  table_ptr = _table;
868  _announce[tid].value = table_ptr;
869 #pragma omp flush
870  } while (table_ptr != _table);
871 
872  /* print buckets */
873  table_ptr->print_buckets();
874 
875  /* reset table announcement to not searching */
876  _announce[tid].value = NULL;
877 }
878 
883 template <class K, class V>
885 
886  /* delete old table */
887  delete _table;
888 
889  /* allocate new table */
890  _table = new FixedHashMap<K,V>(M);
891 }
892 #endif
size_t bucket_count()
Returns the number of buckets in the fixed-size table.
Definition: ParallelHashMap.h:323
void insert(K key, V value)
Insert a given key/value pair into the parallel hash map.
Definition: ParallelHashMap.h:557
void insert(K key, V value)
Inserts a key/value pair into the fixed-size table.
Definition: ParallelHashMap.h:241
K * keys()
Returns an array of the keys in the fixed-size table.
Definition: ParallelHashMap.h:338
bool contains(K &key)
Determine whether the parallel hash map contains a given key.
Definition: ParallelHashMap.h:480
ParallelHashMap(size_t M=64, size_t L=64)
Constructor generates initial underlying table as a fixed-sized hash map and initializes concurrency ...
Definition: ParallelHashMap.h:435
FixedHashMap(size_t M=64)
Constructor initializes fixed-size table of buckets filled with empty linked lists.
Definition: ParallelHashMap.h:138
A fixed-size hash map supporting insertion and lookup operations.
Definition: ParallelHashMap.h:33
long insert_and_get_count(K key, V value)
Insert a given key/value pair into the parallel hash map and return the order number.
Definition: ParallelHashMap.h:622
V at(K &key)
Determine the value associated with a given key.
Definition: ParallelHashMap.h:518
V * values()
Returns an array of the values in the underlying table.
Definition: ParallelHashMap.h:797
size_t num_locks()
Returns the number of locks in the parallel hash map.
Definition: ParallelHashMap.h:746
Definition: log.h:88
void clear()
Clears all key/value pairs form the hash table.
Definition: ParallelHashMap.h:390
K * keys()
Returns an array of the keys in the underlying table.
Definition: ParallelHashMap.h:762
virtual ~ParallelHashMap()
Destructor frees memory associated with fixed-sized hash map and concurrency structures.
Definition: ParallelHashMap.h:460
long insert_and_get_count(K key, V value)
Inserts a key/value pair into the fixed-size table and returns the order number with which it was ins...
Definition: ParallelHashMap.h:279
void update(K &key, V value)
Updates the value associated with a key in the parallel hash map.
Definition: ParallelHashMap.h:591
void setFixedSize()
Prevents the parallel hash map from further resizing.
Definition: ParallelHashMap.h:825
virtual ~FixedHashMap()
Destructor deletes all nodes in the linked lists associated with each bucket in the fixed-size table ...
Definition: ParallelHashMap.h:161
V * values()
Returns an array of the values in the fixed-size table.
Definition: ParallelHashMap.h:367
size_t bucket_count()
Returns the number of buckets in the underlying table.
Definition: ParallelHashMap.h:736
bool contains(K &key)
Determine whether the fixed-size table contains a given key.
Definition: ParallelHashMap.h:185
void log_printf(logLevel level, const char *format,...)
Print a formatted message to the console.
Definition: log.cpp:300
void realloc(size_t M)
Reallocates the underlying table to the desired size.
Definition: ParallelHashMap.h:884
V & at(K &key)
Determine the value associated with a given key in the fixed-size table.
Definition: ParallelHashMap.h:212
void print_buckets()
Prints the contents of each bucket to the screen.
Definition: ParallelHashMap.h:419
size_t size()
Returns the number of key/value pairs in the underlying table.
Definition: ParallelHashMap.h:726
void print_buckets()
Prints the contents of each bucket to the screen.
Definition: ParallelHashMap.h:858
size_t size()
Returns the number of key/value pairs in the fixed-size table.
Definition: ParallelHashMap.h:313
Utility functions for writing log messages to the screen.
A thread-safe hash map supporting insertion and lookup operations.
Definition: ParallelHashMap.h:76
void clear()
Clears all key/value pairs form the hash table.
Definition: ParallelHashMap.h:834
Definition: log.h:58