CMS 3D CMS Logo

OtherHashMaps.h
Go to the documentation of this file.
1 #ifndef TrackingTools_TrajectoryCleaning_src_OtherHashMaps
2 #define TrackingTools_TrajectoryCleaning_src_OtherHashMaps
3 
4 #include <functional>
5 #include <iostream>
6 #include <iterator>
7 #include <list>
8 #include <map>
9 #include <memory>
10 #include <type_traits>
11 #include <vector>
12 
13 namespace cmsutil {
14 
15  /*** The concept is the same of std::map<K, std::vector<V>>, but the implementation is much different (and the interface is not really compatible)
16  * It works only for K and V objects with trivial destructors (primitive types and bare pointers are fine)
17  * The main implementation difference w.r.t. unordered_map<K, std::vector<V>> is that this map is optimized to do very few memory allocations
18  * (in particular, clear does not redeme memory so if you just clear the map instead of deleting it you won't call malloc each time you fill it)
19  * The map doesn't rehash, so you should set a reasonable number of buckets (that you can change also when clearing the map)
20  * Note that too many buckets will make your map slow to clear (and possibly more prone to cache misses).
21  * Although it can take an allocator as template argument, it has been tested only with std::allocator.
22  * When used in the TrajectoryCleanerBySharedHits it works and it does improve the performance. Any other usecase was not checked at all.
23  * */
24  template <typename K,
25  typename V,
26  typename Hasher = std::hash<K>,
27  typename Equals = std::equal_to<K>,
28  typename Alloc = std::allocator<V> >
30  // taking Alloc definition from http://www.codeproject.com/KB/cpp/allocator.aspx
31  static_assert(std::conjunction<std::is_trivially_destructible<K>, std::is_trivially_destructible<V> >::value);
32 
33  public:
34  typedef Hasher hasher;
36  typedef typename std::conditional<std::is_pointer<V>::value, V, V const &>::type
37  value_ref; // otherwise we get problems with 'const' if V is a pointer
38 
39  SimpleAllocHashMultiMap(size_t buckets, size_t keyRowSize, size_t valueRowSize, size_t maxRows = 50);
40 
42 
43  void clear(size_t newBucketSize = 0) {
44  if (newBucketSize != 0) {
45  if (newBucketSize > bucketCapacity_) {
46  ptrAlloc_.deallocate(buckets_, bucketCapacity_);
47  bucketCapacity_ = newBucketSize;
49  }
50  bucketSize_ = newBucketSize;
51  }
52  memset(buckets_, 0, bucketSize_ * sizeof(KeyItem *));
53  currentKeyRow_ = keyRows_.begin();
54  nextKeyItem_ = keyRows_.front();
56  currentValueRow_ = valueRows_.begin();
57  nextValueItem_ = valueRows_.front();
59  if ((keyRows_.size() > maxRows_) || (valueRows_.size() > maxRows_))
60  freeRows();
61  }
62  void freeRows();
63 
64  bool empty() const { return nextKeyItem_ == keyRows_.front(); }
65 
66  void insert(K const &key, value_ref value);
67 
68  struct ValueItem {
69  ValueItem(ValueItem *next1, value_ref val) : next(next1), value(val) {}
72  typedef V value_type;
73  const value_type &operator()() const { return value; }
74  };
75 
76  struct KeyItem {
77  KeyItem(KeyItem *next1, K const &key1, ValueItem *val1) : key(key1), next(next1), value(val1) {}
78  K key;
82  const value_type &operator()() const { return *this; }
83  };
84 
85  template <typename Item>
86  class item_iterator {
87  public:
88  typedef ::std::forward_iterator_tag iterator_category;
89  typedef const typename Item::value_type value_type;
90  typedef const value_type &reference;
91  typedef const value_type *pointer;
92  typedef ptrdiff_t difference_type;
94 
95  item_iterator() : it_(nullptr) {}
96  item_iterator(const Item *it) : it_(it) {}
97  const value_type &operator*() const { return (*it_)(); }
98  const value_type *operator->() const { return &(*it_)(); }
100  if (it_ != nullptr)
101  it_ = it_->next;
102  return *this;
103  }
104  bool operator==(const self_type &other) const { return it_ == other.it_; }
105  bool operator!=(const self_type &other) const { return it_ != other.it_; }
106  bool good() const { return (it_ != nullptr); }
107 
108  private:
109  const Item *it_;
110  };
112 
113  value_iterator values(K const &key);
114 
115  private:
119 
120  // --- buckets ---
123  // --- keys ---
124  size_t keyRowSize_;
125  std::list<KeyItem *> keyRows_;
126  typename std::list<KeyItem *>::iterator
127  currentKeyRow_; // last row that is currently in use. nextItem_ and the last valid item are both on this row. it is never rows_.end()
129  // --- values ---
131  std::list<ValueItem *> valueRows_;
132  typename std::list<ValueItem *>::iterator
133  currentValueRow_; // last row that is currently in use. nextItem_ and the last valid item are both on this row. it is never rows_.end()
135  // --- other ---
136  size_t maxRows_;
137  Hasher hasher_;
138  Equals eq_;
142 
143  KeyItem *push_back_(K const &key, KeyItem *next);
145  KeyItem &find_or_insert_(K const &key);
146 
147  public:
148  void dump() {
149  std::cout << "Dumping HASH MULTIMAP" << std::endl;
150  std::cout << " Key items: "
152  << std::endl;
153  std::cout << " Value items: "
156  << std::endl;
157  size_t row = 0;
158  std::cout << " Buckets (size " << bucketSize_ << ", capacity " << bucketCapacity_ << ")";
159  for (KeyItem **p = buckets_; p != buckets_ + bucketSize_; ++p, ++row) {
160  std::cout << " [" << row << "] " << *p << std::endl;
161  }
162  row = 0;
163  std::cout << " Key Items " << std::endl;
164  for (typename std::list<KeyItem *>::iterator it = keyRows_.begin(), last = keyRows_.end(); it != last;
165  ++it, ++row) {
166  KeyItem *lastI = *it + keyRowSize_;
167  std::cout << " Key Row " << row << " (of size " << keyRowSize_ << ")" << std::endl;
168  for (KeyItem *p = *it; p != lastI; ++p) {
169  std::cout << " @ " << p << " [" << p->key << ", @" << p->value << "], next = " << p->next << std::endl;
170  if ((it == currentKeyRow_) && (p == nextKeyItem_ - 1)) {
171  std::cout << " ^^^ this was the last valid item." << std::endl;
172  last = 0;
173  break;
174  }
175  }
176  if (lastI == 0)
177  break;
178  }
179  row = 0;
180  std::cout << " Value Items " << std::endl;
181  for (typename std::list<ValueItem *>::iterator it = valueRows_.begin(), last = valueRows_.end(); it != last;
182  ++it, ++row) {
183  ValueItem *lastI = *it + valueRowSize_;
184  std::cout << " Value Row " << row << " (of size " << valueRowSize_ << ")" << std::endl;
185  for (ValueItem *p = *it; p != lastI; ++p) {
186  std::cout << " @ " << p << " [" << p->value << "], next = " << p->next << std::endl;
187  if ((it == currentValueRow_) && (p == nextValueItem_ - 1)) {
188  std::cout << " ^^^ this was the last valid item." << std::endl;
189  last = 0;
190  break;
191  }
192  }
193  if (lastI == 0)
194  break;
195  }
196  std::cout << " End of dump" << std::endl;
197  }
198  };
199 
200  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
202  size_t keyRowSize,
203  size_t valueRowSize,
204  size_t maxRows)
205  : bucketSize_(buckets),
206  bucketCapacity_(bucketSize_),
207  keyRowSize_(keyRowSize),
208  valueRowSize_(valueRowSize),
209  maxRows_(maxRows) {
210  buckets_ = ptrAlloc_.allocate(bucketCapacity_);
211  keyRows_.push_back(keyAlloc_.allocate(keyRowSize_));
212  valueRows_.push_back(valueAlloc_.allocate(valueRowSize_));
213  clear();
214  }
215  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
217  for (typename std::list<KeyItem *>::iterator it = keyRows_.begin(), last = keyRows_.end(); it != last; ++it) {
218  keyAlloc_.deallocate(*it, keyRowSize_);
219  }
220  for (typename std::list<ValueItem *>::iterator it = valueRows_.begin(), last = valueRows_.end(); it != last; ++it) {
221  valueAlloc_.deallocate(*it, valueRowSize_);
222  }
223  ptrAlloc_.deallocate(buckets_, bucketCapacity_);
224  }
225 
226  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
228  if (keyRows_.size() > maxRows_) {
229  //std::cerr << "Freeing key rows, current size is " << keyRows_.size() << std::endl;
230  typename std::list<KeyItem *>::iterator it = keyRows_.begin(), last = keyRows_.end();
231  for (std::advance(it, maxRows_); it != last; ++it) {
232  keyAlloc_.deallocate(*it, keyRowSize_);
233  }
234  keyRows_.resize(maxRows_);
235  }
236  if (valueRows_.size() > maxRows_) {
237  //std::cerr << "Freeing value rows, current size is " << valueRows_.size() << std::endl;
238  typename std::list<ValueItem *>::iterator it = valueRows_.begin(), last = valueRows_.end();
239  for (std::advance(it, maxRows_); it != last; ++it) {
240  valueAlloc_.deallocate(*it, valueRowSize_);
241  }
242  valueRows_.resize(maxRows_);
243  }
244  }
245 
246  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
249  if (nextKeyItem_ == keyEndMarker_) {
250  ++currentKeyRow_;
251  if (currentKeyRow_ == keyRows_.end()) {
252  keyRows_.push_back(keyAlloc_.allocate(keyRowSize_));
253  currentKeyRow_ = keyRows_.end();
254  --currentKeyRow_; // end - 1 doesn't work!
255  }
256  nextKeyItem_ = *currentKeyRow_;
257  keyEndMarker_ = nextKeyItem_ + keyRowSize_;
258  }
259  keyAlloc_.construct(nextKeyItem_, KeyItem(next, key, nullptr));
260  nextKeyItem_++;
261  return (nextKeyItem_ - 1);
262  }
263  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
266  if (nextValueItem_ == valueEndMarker_) {
267  ++currentValueRow_;
268  if (currentValueRow_ == valueRows_.end()) {
269  valueRows_.push_back(valueAlloc_.allocate(valueRowSize_));
270  currentValueRow_ = valueRows_.end();
271  --currentValueRow_; // end - 1 doesn't work!
272  }
273  nextValueItem_ = *currentValueRow_;
274  valueEndMarker_ = nextValueItem_ + valueRowSize_;
275  }
276  valueAlloc_.construct(nextValueItem_, ValueItem(next, value));
277  nextValueItem_++;
278  return (nextValueItem_ - 1);
279  }
280 
281  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
284  //std::cout << "Find or insert for key " << key << std::endl;
285  size_t hash = hasher_(key);
286  KeyItem *&buck = buckets_[hash % bucketSize_];
287  KeyItem *curr = buck;
288  while (curr) {
289  if (eq_(curr->key, key)) {
290  //std::cout << " Key " << key << " was found." << std::endl;
291  return *curr;
292  }
293  curr = curr->next;
294  }
295  buck = push_back_(key, buck);
296  return *buck;
297  }
298 
299  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
301  //std::cout << "Pushing back value " << value << " for key " << key << std::endl;
302  KeyItem &k = find_or_insert_(key);
303  //std::cout << "Key " << (k.value ? "exists" : " is new") << std::endl;
304  k.value = push_back_(value, k.value);
305  }
306 
307  template <typename K, typename V, typename Hasher, typename Equals, typename Alloc>
310  //std::cout << "Gettinv values for key " << key << std::endl;
311  size_t hash = hasher_(key);
312  for (KeyItem *curr = buckets_[hash % bucketSize_]; curr; curr = curr->next) {
313  if (eq_(curr->key, key))
314  return value_iterator(curr->value);
315  }
316  return value_iterator();
317  }
318 
319  /*** Very very simple map implementation
320  * It's just a std::vector<pair<key,value>>, and the operator[] does a linear search to find the key (it's O(N) time, both if the key exists and if it doesn't)
321  * Anyway, if your map is very small and if you clear it often, it performs better than more complex variants
322  * Semantics is as std::map, except that only very few methods are implemented.
323  * */
324  template <typename K, typename V>
326  public:
327  typedef std::pair<K, V> value_type;
328  typedef typename std::vector<value_type>::const_iterator const_iterator;
329  typedef typename std::vector<value_type>::iterator iterator; // but please don't mutate the keys
330 
331  void clear() { data_.clear(); }
332  bool empty() const { return data_.empty(); }
333  const_iterator begin() const { return data_.begin(); }
334  const_iterator end() const { return data_.end(); }
335  iterator begin() { return data_.begin(); }
336  iterator end() { return data_.end(); }
337 
338  V &operator[](const K &k) {
339  for (typename std::vector<value_type>::iterator it = data_.begin(), ed = data_.end(); it != ed; ++it) {
340  if (it->first == k)
341  return it->second;
342  }
343  data_.push_back(value_type(k, V()));
344  return data_.back().second;
345  }
346 
348 
349  private:
350  std::vector<value_type> data_;
351  };
352 
353 } // namespace cmsutil
354 
355 #endif
item_iterator< ValueItem > value_iterator
std::conditional< std::is_pointer< V >::value, V, V const & >::type value_ref
Definition: OtherHashMaps.h:37
std::list< ValueItem * > valueRows_
KeyItem & find_or_insert_(K const &key)
const_iterator end() const
Alloc::template rebind< KeyItem >::other KeyItemAllocator
uint32_t T const *__restrict__ uint32_t const *__restrict__ int32_t int Histo::index_type cudaStream_t V
bool operator!=(const self_type &other) const
SimpleAllocHashMultiMap< K, V, Hasher, Equals, Alloc > map_type
Definition: OtherHashMaps.h:35
::std::forward_iterator_tag iterator_category
Definition: OtherHashMaps.h:88
std::list< ValueItem * >::iterator currentValueRow_
KeyItem * push_back_(K const &key, KeyItem *next)
ValueItem(ValueItem *next1, value_ref val)
Definition: OtherHashMaps.h:69
def template(fileName, svg, replaceme="REPLACEME")
Definition: svgfig.py:521
bool operator==(const self_type &other) const
void insert(K const &key, value_ref value)
const value_type & operator()() const
Definition: OtherHashMaps.h:73
Definition: value.py:1
value_iterator values(K const &key)
Alloc::template rebind< ValueItem >::other ValueItemAllocator
std::vector< value_type >::const_iterator const_iterator
std::vector< value_type > data_
std::vector< value_type >::iterator iterator
const value_type & operator()() const
Definition: OtherHashMaps.h:82
const_iterator begin() const
Alloc::template rebind< KeyItem * >::other KeyItemPtrAllocator
void clear(size_t newBucketSize=0)
Definition: OtherHashMaps.h:43
std::list< KeyItem * > keyRows_
KeyItem(KeyItem *next1, K const &key1, ValueItem *val1)
Definition: OtherHashMaps.h:77
SimpleAllocHashMultiMap(size_t buckets, size_t keyRowSize, size_t valueRowSize, size_t maxRows=50)
std::list< KeyItem * >::iterator currentKeyRow_