The Sparta Modeling Framework
Loading...
Searching...
No Matches
TieredMap.hpp
1// <TieredMap> -*- C++ -*-
2
3#pragma once
4
5#include <iostream>
6#include <utility>
7#include <memory>
8
9#include "sparta/utils/Utils.hpp"
12
13
14namespace sparta
15{
38 template <typename KeyT=uint64_t, typename ValT=void*>
40 {
41 public:
42
46
52 typedef std::pair<const KeyT, ValT> pair_t;
53
64 typedef std::vector<void*> node_t;
65
66 // Forward declaration for friendship with iterator
67 class const_iterator;
68
74 class iterator {
75 public:
76 typedef typename std::vector<std::unique_ptr<pair_t>>::iterator internal_iter;
77
78 iterator(internal_iter itr) :
79 itr_(itr)
80 { }
81
82 iterator& operator++() { ++itr_; return *this; }
83 iterator operator++(int) { iterator ret(itr_); ++itr_; return ret; }
84
85 iterator& operator=(const iterator& rhp) {
86 itr_ = rhp.itr_;
87 return *this;
88 }
89
90 bool operator==(const iterator& rhp) const {
91 return rhp.itr_ == itr_;
92 }
93
94 bool operator!=(const iterator& rhp) const {
95 return rhp.itr_ != itr_;
96 }
97
98 bool operator==(const const_iterator& rhp) const {
99 return rhp.itr_ == itr_;
100 }
101
102 bool operator!=(const const_iterator& rhp) const {
103 return rhp.itr_ != itr_;
104 }
105
106 ValT& operator*() { return (*itr_)->second; }
107 const ValT& operator*() const { return (*itr_)->second; }
108
109 ValT* operator->() { return &(*itr_)->second; }
110 const ValT* operator->() const { return &(*itr_)->second; }
111
112 friend class const_iterator;
113
114 private:
115 internal_iter itr_;
116 };
117
124 public:
125 typedef typename std::vector<std::unique_ptr<pair_t>>::const_iterator internal_iter;
126
127 const_iterator(internal_iter itr) :
128 itr_(itr)
129 { }
130
131 const_iterator& operator++() { ++itr_; return *this; }
132 const_iterator operator++(int) { const_iterator ret(itr_); ++itr_; return ret; }
133
134 const_iterator& operator=(const const_iterator& rhp) {
135 itr_ = rhp.itr_;
136 return *this;
137 }
138
139 bool operator==(const const_iterator& rhp) const {
140 return rhp.itr_ == itr_;
141 }
142
143 bool operator!=(const const_iterator& rhp) const {
144 return rhp.itr_ != itr_;
145 }
146
147 bool operator==(const iterator& rhp) const {
148 return rhp.itr_ == itr_;
149 }
150
151 bool operator!=(const iterator& rhp) const {
152 return rhp.itr_ != itr_;
153 }
154
155 const ValT& operator*() const { return (*itr_)->second; }
156
157 const ValT* operator->() const { return &(*itr_)->second; }
158
159 friend class iterator;
160
161 private:
162 internal_iter itr_;
163 };
164
167
171
176 TieredMap(uint64_t node_size=512) :
177 total_size_(0),
178 num_nodes_(0), // First node is allocated in ctor
179 num_tiers_(0), // First tier is allocated in ctor
180 node_size_(node_size),
181 tier_shift_(0),
182 tier_idx_mask_(~computeMask<uint64_t>(node_size, tier_shift_)) // Computes tier_shift_
183 {
184 if(false == isPowerOf2(node_size_)){
185 throw SpartaException("node_size must be a power of 2, is ")
186 << node_size;
187 }
188
189 allocateRoot_();
190 }
191
194 // Free all nodes
195 freeNode_(t0_);
196 }
197
200
204
208 uint64_t size() const { return pairs_.size(); }
209
215 uint64_t getNumTiers() const { return num_tiers_; }
216
220 uint64_t getNumNodes() const { return num_nodes_; }
221
225 uint64_t getEstimatedMemory() const {
226 return sizeof(TieredMap) // This map
227 + (num_nodes_ * sizeof(node_t)) // Each node (container)
228 + (num_nodes_ * sizeof(void*) * 0.7) // Content of each node. 0.7 for average utilization
229 + (sizeof(pairs_))
230 + (pairs_.size() * sizeof(typename decltype(tier_shifts_)::value_type)) // Pair container and content
231 + (sizeof(tier_shifts_))
232 + (tier_shifts_.size() * sizeof(typename decltype(tier_shifts_)::value_type)); // tier_shift container and content
233 }
234
237
241
242 const_iterator begin() const { return pairs_.begin(); }
243 const_iterator end() const { return pairs_.end(); }
244 iterator begin() { return pairs_.begin(); }
245 iterator end() { return pairs_.end(); }
246
249
253
258 void clear() {
259 freeNode_(t0_);
260 pairs_.clear();
261 tier_shifts_.clear();
262 num_nodes_ = 0;
263 num_tiers_ = 0;
264
265 allocateRoot_(); // Adds first node and tier
266 }
267
275 const pair_t* find(const KeyT& k) const {
276 const pair_t* result = tryGet(k);
277 if(!result){
278 return nullptr;
279 }
280 return result;
281 }
282
284 pair_t* find(const KeyT& k) {
285 pair_t* result = tryGet(k);
286 if(!result){
287 return nullptr;
288 }
289 return result;
290 }
291
298 ValT& operator[](const KeyT& k) {
299 pair_t* result = tryGet(k);
300 if(result){
301 return (result->second);
302 }
303 return set_(k, 0);
304 }
305
307 const ValT& operator[](const KeyT& k) const {
308 pair_t* result = tryGet(k);
309 if(result){
310 return (result->second);
311 }
312 return set_(k, 0);
313 }
314
322 pair_t* tryGet(const KeyT& k) {
323 void* v = t0_;
324 size_t tidx = 0;
325
326 uint64_t vidx = k >> tier_shifts_[tidx];
327 if(vidx >= node_size_){
328 return nullptr;
329 }
330
331 do{
332 vidx = k >> tier_shifts_[tidx];
333 vidx &= tier_idx_mask_;
334
335 node_t& node = *static_cast<node_t*>(v);
336 if(vidx >= node.size()){
337 return nullptr;
338 }
339 v = node[vidx]; // Points to node in next tier or final pair result
340 if(!v){
341 return nullptr;
342 }
343 ++tidx;
344 }while(tidx < num_tiers_);
345
346 return static_cast<pair_t*>(v);
347 }
348
352 const pair_t* tryGet(const KeyT& k) const {
353 void* v = t0_;
354 size_t tidx = 0;
355
356 uint64_t vidx = k >> tier_shifts_[tidx];
357 if(vidx >= node_size_){
358 return nullptr;
359 }
360
361 do{
362 vidx = k >> tier_shifts_[tidx];
363 vidx &= tier_idx_mask_;
364
365 const node_t& node = *static_cast<const node_t*>(v);
366 if(vidx >= node.size()){
367 return nullptr;
368 }
369 v = node[vidx]; // Points to next tier or final pair result
370 if(!v){
371 return nullptr;
372 }
373 ++tidx;
374 }while(tidx < num_tiers_);
375
376 return static_cast<const pair_t*>(v);
377 }
378
381
382 private:
383
390 void allocateRoot_() {
391 sparta_assert(num_nodes_ == 0
392 && num_tiers_ == 0
393 && tier_shifts_.size() == 0,
394 "num_nodes_, num_tiers_, and tier_shifts_.size() must be 0 during allocateRoot_");
395 tier_shifts_.push_back(0);
396 sparta_assert(tier_shifts_.size() == 1); // Expects exactly 1 shift to start
397 t0_ = new node_t; // Allocate a node
398 num_nodes_++;
399 num_tiers_++;
400 }
401
412 ValT& set_(const KeyT& k, const ValT n) {
413 void* v = t0_;
414 size_t tidx = 0;
415
416 uint64_t vidx = k >> tier_shifts_[tidx]; // Value index (within tier)
417 if(vidx >= node_size_){
418 // Add a new tier because current tier does not represent a
419 // large enough space to contain this key
420 addTier_();
421 return set_(k, n);
422 }
423
424 do{
425 // Compute index for this tier
426 vidx = k >> tier_shifts_[tidx];
427 vidx &= tier_idx_mask_;
428
429 node_t& node = *static_cast<node_t*>(v);
430 if(vidx >= node.size()){
431 node.resize(vidx+1, nullptr);
432 }
433 void*& vt = node[vidx]; // Points to next tier or final pair result
434 if(!vt){
435 if(tidx < num_tiers_ - 1){
436 //std::cout << "Allocating new vector at tier[0x" << std::hex << vidx << std::dec << "] tidx " << tidx << ", key=0x" << std::hex << k << std::dec << std::endl;
437 // Create a new node at node[vidx]
438 vt = new node_t();
439 ++num_nodes_;
440 }else{
441 //std::cout << "Allocating new pair at tier[0x" << std::hex << vidx << std::dec << "] tidx " << tidx << ", key=0x" << std::hex << k << std::dec << std::endl;
442 // Create a new result pair at node[vidx]
443 pair_t* npair = new pair_t(k, n);
444 pairs_.emplace_back(npair);
445 v = vt = npair;
446 break;
447 }
448 }else{
449 if(tidx == num_tiers_ - 1){
450 pair_t* p = static_cast<pair_t*>(vt);
451 p->second = n;
452 v = p;
453 break;
454 }
455 }
456 v = vt;
457 ++tidx;
458 }while(tidx < num_tiers_);
459
460 return static_cast<pair_t*>(v)->second;
461 }
462
467 void addTier_() {
468 uint64_t new_shift = tier_shifts_[0] + tier_shift_;
469 tier_shifts_.insert(tier_shifts_.begin(), new_shift);
470 void* old = t0_;
471 t0_ = new node_t;
472 t0_->push_back(old); // Old tier at idx=0
473 ++num_nodes_;
474 ++num_tiers_;
475 }
476
482 void freeNode_(node_t* n, uint64_t tier=0) {
483 sparta_assert(n);
484 if(tier < num_tiers_ - 1){
485 for(node_t::iterator i = n->begin(); i != n->end(); ++i){
486 node_t* child = static_cast<node_t*>(*i);
487 if(child){
488 freeNode_(child, tier+1);
489 }
490 }
491 }else{
492 for(node_t::iterator i = n->begin(); i != n->end(); ++i){
493 pair_t* p = static_cast<pair_t*>(*i);
494 if(p){
495 // Do not delete p, it is managed by the pairs_ vector.
496 }
497 }
498 }
499
500 delete n;
501 }
502
504 const uint64_t total_size_;
505
510 uint64_t num_nodes_;
511
516 uint64_t num_tiers_;
517
519 const uint64_t node_size_;
520
522 uint64_t tier_shift_ = 0;
523
528 const uint64_t tier_idx_mask_;
529
534 std::vector<std::unique_ptr<pair_t>> pairs_;
535
537 std::vector<uint64_t> tier_shifts_;
538
544 node_t* t0_;
545
546 }; // class TieredMap
547
548} // namespace sparta
549
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
Exception class for all of Sparta.
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Const for walking contents of this map if const-qualified.
Iterator for walking contents of this map.
Definition TieredMap.hpp:74
N-Tier lookup map for sparse-representation of large memory spaces. This is essentially a M-Tree wher...
Definition TieredMap.hpp:40
std::pair< const KeyT, ValT > pair_t
This map contains mappings between a unique set of keys to values. This type represents that mapping ...
Definition TieredMap.hpp:52
TieredMap(uint64_t node_size=512)
Constructor.
uint64_t getNumTiers() const
Number of tiers used by this map to represent the entire space containing the largest key added....
std::vector< void * > node_t
Node in the map tree. Each node contains up to node_size children, any of which may be.
Definition TieredMap.hpp:64
~TieredMap()
Destructor.
const pair_t * tryGet(const KeyT &k) const
const-qualified version of tryGet.
const ValT & operator[](const KeyT &k) const
const-qualified index operator
ValT & operator[](const KeyT &k)
Finds a value by its key.
const pair_t * find(const KeyT &k) const
Finds a mapping for key k if one exists in this map.
pair_t * find(const KeyT &k)
Overload for find.
uint64_t getNumNodes() const
Returns the number of nodes allocated within this map.
uint64_t size() const
Number of elements allocated in this map.
uint64_t getEstimatedMemory() const
Returns the estimated memory use by this map in bytes.
void clear()
Clears the map. Frees all mappings and data structures.
pair_t * tryGet(const KeyT &k)
Attempts to get a pair_t associated with the key k without modifying the data structure.
Macros for handling exponential backoff.
T computeMask(T size, T &lsb_pos)
Computes a maks and the lsb-position given a block size. The mask generated can be AND-ed with any nu...
Definition Utils.hpp:160
bool isPowerOf2(uint64_t x)
Determines if input is 0 or a power of 2.
Definition Utils.hpp:142