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 <map>
5 #include <vector>
6 #include <memory>
7 #include <list>
8 #include <iterator>
9 #include <boost/unordered_map.hpp>
10 #include <boost/range.hpp>
11 #include <boost/type_traits.hpp>
12 #include <boost/mpl/and.hpp>
13 #include <boost/mpl/logical.hpp>
14 #include <boost/mpl/assert.hpp>
15 #include <boost/mpl/if.hpp>
16 
17 #include <iostream>
18 
19 namespace cmsutil {
20 
21 /*** 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)
22  * It works only for K and V objects with trivial destructors (primitive types and bare pointers are fine)
23  * 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
24  * (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)
25  * The map doesn't rehash, so you should set a reasonable number of buckets (that you can change also when clearing the map)
26  * Note that too many buckets will make your map slow to clear (and possibly more prone to cache misses).
27  * Although it can take an allocator as template argument, it has been tested only with std::allocator.
28  * When used in the TrajectoryCleanerBySharedHits it works and it does improve the performance. Any other usecase was not checked at all.
29  * */
30 template<typename K, typename V, typename Hasher=boost::hash<K>, typename Equals=std::equal_to<K>, typename Alloc=std::allocator<V> >
32  // taking Alloc definition from http://www.codeproject.com/KB/cpp/allocator.aspx
33  BOOST_MPL_ASSERT( (boost::mpl::and_<boost::has_trivial_destructor<K>, boost::has_trivial_destructor<V> > ));
34  public:
35  typedef Hasher hasher;
37  typedef typename boost::mpl::if_<typename boost::is_pointer<V>, V, V const &>::type 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();
55  currentValueRow_ = valueRows_.begin();
57  if ((keyRows_.size() > maxRows_) || (valueRows_.size() > maxRows_)) freeRows();
58  }
59  void freeRows() ;
60 
61  bool empty() const { return nextKeyItem_ == keyRows_.front(); }
62 
63  void insert(K const &key, value_ref value );
64 
65  struct ValueItem {
66  ValueItem(ValueItem *next1, value_ref val) : next(next1), value(val) {}
68  V value;
69  typedef V value_type;
70  const value_type & operator()() const { return value; }
71  };
72 
73  struct KeyItem {
74  KeyItem (KeyItem *next1, K const &key1, ValueItem *val1) : key(key1), next(next1), value(val1) {}
75  K key;
79  const value_type & operator()() const { return *this; }
80  };
81 
82  template<typename Item>
83  class item_iterator {
84  public:
85  typedef ::std::forward_iterator_tag iterator_category;
86  typedef const typename Item::value_type value_type;
87  typedef const value_type & reference;
88  typedef const value_type * pointer;
89  typedef ptrdiff_t difference_type;
91 
92  item_iterator() : it_(nullptr) {}
93  item_iterator(const Item *it) : it_(it) {}
94  const value_type & operator*() const { return (*it_)(); }
95  const value_type * operator->() const { return & (*it_)(); }
96  self_type & operator++() {
97  if (it_ != nullptr) it_ = it_->next;
98  return *this;
99  }
100  bool operator==(const self_type &other) const { return it_ == other.it_; }
101  bool operator!=(const self_type &other) const { return it_ != other.it_; }
102  bool good() const { return (it_ != nullptr); }
103  private:
104  const Item *it_;
105  };
107 
108  value_iterator values(K const &key) ;
109 
110  private:
114 
115  // --- buckets ---
118  // --- keys ---
119  size_t keyRowSize_;
120  std::list<KeyItem *> keyRows_;
121  typename std::list<KeyItem *>::iterator currentKeyRow_; // last row that is currently in use. nextItem_ and the last valid item are both on this row. it is never rows_.end()
123  // --- values ---
125  std::list<ValueItem *> valueRows_;
126  typename std::list<ValueItem *>::iterator currentValueRow_; // last row that is currently in use. nextItem_ and the last valid item are both on this row. it is never rows_.end()
128  // --- other ---
129  size_t maxRows_;
130  Hasher hasher_;
131  Equals eq_;
132  KeyItemAllocator keyAlloc_;
133  ValueItemAllocator valueAlloc_;
134  KeyItemPtrAllocator ptrAlloc_;
135 
136  KeyItem * push_back_(K const &key, KeyItem *next) ;
137  ValueItem * push_back_(value_ref value, ValueItem *next) ;
138  KeyItem & find_or_insert_(K const &key) ;
139  public:
140  void dump() {
141  std::cout << "Dumping HASH MULTIMAP" << std::endl;
142  std::cout << " Key items: " << (std::distance(keyRows_.begin(), currentKeyRow_) * keyRowSize_ + (nextKeyItem_ - *currentKeyRow_)) << std::endl;
143  std::cout << " Value items: " << (std::distance(valueRows_.begin(), currentValueRow_) * valueRowSize_ + (nextValueItem_ - *currentValueRow_)) << std::endl;
144  size_t row = 0;
145  std::cout << " Buckets (size " << bucketSize_ << ", capacity " << bucketCapacity_ << ")";
146  for (KeyItem **p = buckets_; p != buckets_ + bucketSize_; ++p, ++row) {
147  std::cout << " ["<<row<<"] " << *p << std::endl;
148  }
149  row = 0;
150  std::cout << " Key Items " << std::endl;
151  for (typename std::list<KeyItem *>::iterator it = keyRows_.begin(), last = keyRows_.end(); it != last; ++it, ++row) {
152  KeyItem *lastI = *it + keyRowSize_;
153  std::cout << " Key Row " << row << " (of size " << keyRowSize_ << ")" << std::endl;
154  for (KeyItem *p = *it; p != lastI; ++p) {
155  std::cout << " @ " << p << " [" << p->key << ", @" << p->value <<"], next = " << p->next << std::endl;
156  if ((it == currentKeyRow_) && (p == nextKeyItem_ - 1)) {
157  std::cout << " ^^^ this was the last valid item." << std::endl;
158  last = 0; break;
159  }
160  }
161  if (lastI == 0) break;
162  }
163  row = 0;
164  std::cout << " Value Items " << std::endl;
165  for (typename std::list<ValueItem *>::iterator it = valueRows_.begin(), last = valueRows_.end(); it != last; ++it, ++row) {
166  ValueItem *lastI = *it + valueRowSize_;
167  std::cout << " Value Row " << row << " (of size " << valueRowSize_ << ")" << std::endl;
168  for (ValueItem *p = *it; p != lastI; ++p) {
169  std::cout << " @ " << p << " [" << p->value <<"], next = " << p->next << std::endl;
170  if ((it == currentValueRow_) && (p == nextValueItem_ - 1)) {
171  std::cout << " ^^^ this was the last valid item." << std::endl;
172  last = 0; break;
173  }
174  }
175  if (lastI == 0) break;
176  }
177  std::cout << " End of dump" << std::endl;
178 
179  }
180 };
181 
182 template<typename K, typename V, typename Hasher, typename Equals, typename Alloc>
183 SimpleAllocHashMultiMap<K,V,Hasher,Equals,Alloc>::SimpleAllocHashMultiMap(size_t buckets, size_t keyRowSize, size_t valueRowSize, size_t maxRows) :
185  keyRowSize_(keyRowSize),
186  valueRowSize_(valueRowSize),
187  maxRows_(maxRows)
188 {
189  buckets_ = ptrAlloc_.allocate(bucketCapacity_);
190  keyRows_.push_back(keyAlloc_.allocate(keyRowSize_));
191  valueRows_.push_back(valueAlloc_.allocate(valueRowSize_));
192  clear();
193 }
194 template<typename K, typename V, typename Hasher, typename Equals, typename Alloc>
196 {
197  for (typename std::list<KeyItem *>::iterator it = keyRows_.begin(), last = keyRows_.end(); it != last; ++it) {
198  keyAlloc_.deallocate(*it, keyRowSize_);
199  }
200  for (typename std::list<ValueItem *>::iterator it = valueRows_.begin(), last = valueRows_.end(); it != last; ++it) {
201  valueAlloc_.deallocate(*it, valueRowSize_);
202  }
203  ptrAlloc_.deallocate(buckets_, bucketCapacity_);
204 }
205 
206 template<typename K, typename V, typename Hasher, typename Equals, typename Alloc>
207 void
209  if (keyRows_.size() > maxRows_) {
210  //std::cerr << "Freeing key rows, current size is " << keyRows_.size() << std::endl;
211  typename std::list<KeyItem *>::iterator it = keyRows_.begin(), last = keyRows_.end();
212  for (std::advance(it, maxRows_); it != last; ++it) {
213  keyAlloc_.deallocate(*it, keyRowSize_);
214  }
215  keyRows_.resize(maxRows_);
216  }
217  if (valueRows_.size() > maxRows_) {
218  //std::cerr << "Freeing value rows, current size is " << valueRows_.size() << std::endl;
219  typename std::list<ValueItem *>::iterator it = valueRows_.begin(), last = valueRows_.end();
220  for (std::advance(it, maxRows_); it != last; ++it) {
221  valueAlloc_.deallocate(*it, valueRowSize_);
222  }
223  valueRows_.resize(maxRows_);
224  }
225 }
226 
227 template<typename K, typename V, typename Hasher, typename Equals, typename Alloc>
230  if (nextKeyItem_ == keyEndMarker_) {
231  ++currentKeyRow_;
232  if (currentKeyRow_ == keyRows_.end()) {
233  keyRows_.push_back(keyAlloc_.allocate(keyRowSize_));
234  currentKeyRow_ = keyRows_.end(); --currentKeyRow_; // end - 1 doesn't work!
235  }
238  }
239  keyAlloc_.construct(nextKeyItem_, KeyItem(next, key, nullptr));
240  nextKeyItem_++;
241  return (nextKeyItem_-1);
242 }
243 template<typename K, typename V, typename Hasher, typename Equals, typename Alloc>
247  ++currentValueRow_;
248  if (currentValueRow_ == valueRows_.end()) {
249  valueRows_.push_back(valueAlloc_.allocate(valueRowSize_));
250  currentValueRow_ = valueRows_.end(); --currentValueRow_; // end - 1 doesn't work!
251  }
254  }
255  valueAlloc_.construct(nextValueItem_, ValueItem(next, value));
256  nextValueItem_++;
257  return (nextValueItem_-1);
258 }
259 
260 template<typename K, typename V, typename Hasher, typename Equals, typename Alloc>
263  //std::cout << "Find or insert for key " << key << std::endl;
264  size_t hash = hasher_(key);
265  KeyItem * & buck = buckets_[hash % bucketSize_];
266  KeyItem * curr = buck;
267  while (curr) {
268  if (eq_(curr->key, key)) {
269  //std::cout << " Key " << key << " was found." << std::endl;
270  return *curr;
271  }
272  curr = curr->next;
273  }
274  buck = push_back_(key, buck);
275  return *buck;
276 }
277 
278 template<typename K, typename V, typename Hasher, typename Equals, typename Alloc>
280  //std::cout << "Pushing back value " << value << " for key " << key << std::endl;
281  KeyItem &k = find_or_insert_(key);
282  //std::cout << "Key " << (k.value ? "exists" : " is new") << std::endl;
283  k.value = push_back_(value, k.value);
284 }
285 
286 template<typename K, typename V, typename Hasher, typename Equals, typename Alloc>
289  //std::cout << "Gettinv values for key " << key << std::endl;
290  size_t hash = hasher_(key);
291  for (KeyItem * curr = buckets_[hash % bucketSize_]; curr; curr = curr->next) {
292  if (eq_(curr->key, key)) return value_iterator(curr->value);
293  }
294  return value_iterator();
295 }
296 
297 
298 /*** Very very simple map implementation
299  * 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)
300  * Anyway, if your map is very small and if you clear it often, it performs better than more complex variants
301  * Semantics is as std::map, except that only very few methods are implemented.
302  * */
303 template<typename K, typename V>
305  public:
306  typedef std::pair<K,V> value_type;
307  typedef typename std::vector<value_type>::const_iterator const_iterator;
308  typedef typename std::vector<value_type>::iterator iterator; // but please don't mutate the keys
309 
310  void clear() { data_.clear(); }
311  bool empty() const { return data_.empty(); }
312  const_iterator begin() const { return data_.begin(); }
313  const_iterator end() const { return data_.end(); }
314  iterator begin() { return data_.begin(); }
315  iterator end() { return data_.end(); }
316 
317  V & operator[](const K &k) {
318  for (typename std::vector<value_type>::iterator it = data_.begin(), ed = data_.end(); it != ed; ++it) {
319  if (it->first == k) return it->second;
320  }
321  data_.push_back(value_type(k,V()));
322  return data_.back().second;
323  }
324 
325 
327 
328  private:
329  std::vector<value_type> data_;
330 
331 };
332 
333 } // namespace
334 
335 #endif
type
Definition: HCALResponse.h:21
item_iterator< ValueItem > value_iterator
std::list< ValueItem * > valueRows_
KeyItem & find_or_insert_(K const &key)
#define nullptr
const_iterator begin() const
const_iterator end() const
Alloc::template rebind< KeyItem >::other KeyItemAllocator
bool operator!=(const self_type &other) const
BOOST_MPL_ASSERT((boost::mpl::and_< boost::has_trivial_destructor< K >, boost::has_trivial_destructor< V > >))
::std::forward_iterator_tag iterator_category
Definition: OtherHashMaps.h:85
std::list< ValueItem * >::iterator currentValueRow_
KeyItem * push_back_(K const &key, KeyItem *next)
ValueItem(ValueItem *next1, value_ref val)
Definition: OtherHashMaps.h:66
def template(fileName, svg, replaceme="REPLACEME")
Definition: svgfig.py:521
void insert(K const &key, value_ref value)
Definition: value.py:1
value_iterator values(K const &key)
boost::mpl::if_< typename boost::is_pointer< V >, V, V const & >::type value_ref
Definition: OtherHashMaps.h:37
Alloc::template rebind< ValueItem >::other ValueItemAllocator
int k[5][pyjets_maxn]
SimpleAllocHashMultiMap< K, V, Hasher, Equals, Alloc > map_type
Definition: OtherHashMaps.h:36
std::vector< value_type >::const_iterator const_iterator
const value_type & operator()() const
Definition: OtherHashMaps.h:70
const value_type & operator()() const
Definition: OtherHashMaps.h:79
std::vector< value_type > data_
std::vector< value_type >::iterator iterator
Alloc::template rebind< KeyItem * >::other KeyItemPtrAllocator
bool operator==(const self_type &other) const
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:74
SimpleAllocHashMultiMap(size_t buckets, size_t keyRowSize, size_t valueRowSize, size_t maxRows=50)
std::list< KeyItem * >::iterator currentKeyRow_