The Sparta Modeling Framework
Loading...
Searching...
No Matches
CircularBuffer.hpp
Go to the documentation of this file.
1// <CircularBuffer.h> -*- C++ -*-
2
3
11#pragma once
12
13#include <cinttypes>
14#include <list>
15#include <limits>
16#include <algorithm>
17
19#include "sparta/statistics/CycleCounter.hpp"
22#include "sparta/collection/IterableCollector.hpp"
23#include "sparta/statistics/Counter.hpp"
25
26namespace sparta
27{
28
84 template <class DataT>
86 {
87 public:
88
89 // A typedef for this CircularBuffer's type. This is useful for my
90 // subclasses, CircularBufferIterator & EntryValidator
92
93 // Typedef for the DataT
94 typedef DataT value_type;
95
96 // Typedef for size_type
97 typedef uint32_t size_type;
98
99 private:
100
101 struct CircularBufferData
102 {
103 template<typename U = value_type>
104 CircularBufferData(U && dat,
105 const uint64_t window_idx) :
106 data(std::forward<U>(dat)),
107 window_idx(window_idx)
108 {}
109 CircularBufferData(const uint64_t window_idx) :
110 window_idx(window_idx)
111 {}
112 value_type data{}; // The data supplied by the user
113 uint64_t window_idx; // The location in the validity
114 // window of the CB. Serves as a
115 // fast check for validity of
116 // outstanding iterator
117 };
118 using CircularBufferDataStorage = std::list<CircularBufferData>;
119 typedef typename CircularBufferDataStorage::iterator InternalCircularBufferIterator;
120 typedef typename CircularBufferDataStorage::const_iterator InternalCircularBufferConstIterator;
121
139 template <bool is_const_iterator = true>
140 class CircularBufferIterator :
141 public utils::IteratorTraits<std::bidirectional_iterator_tag, value_type>
142 {
143 private:
144 // Constant to indicate constness
145 static constexpr bool is_const_iterator_type = is_const_iterator;
146
147 friend class CircularBuffer<value_type>;
148 typedef typename std::conditional<is_const_iterator,
149 const value_type &, value_type &>::type DataReferenceType;
150 typedef typename std::conditional<is_const_iterator,
151 const CircularBufferType *, CircularBufferType *>::type CircularBufferPointerType;
152
153 // Pointer to the CircularBuffer which created this entry
154 CircularBufferPointerType attached_circularbuffer_ = nullptr;
155
156 typedef typename std::conditional<is_const_iterator,
157 InternalCircularBufferConstIterator,
158 InternalCircularBufferIterator>::type SpecificCircularBufferIterator;
159 SpecificCircularBufferIterator circularbuffer_entry_;
160
162 uint64_t window_idx_ = std::numeric_limits<uint64_t>::max();
163
169 CircularBufferIterator(CircularBufferPointerType circularbuffer,
170 SpecificCircularBufferIterator entry,
171 const uint64_t window_idx) :
172 attached_circularbuffer_(circularbuffer),
173 circularbuffer_entry_(entry),
174 window_idx_(window_idx)
175 {}
176
177 // Used by the CircularBuffer to get the position in the
178 // internal CircularBuffer
179 SpecificCircularBufferIterator getInternalBufferEntry_() const {
180 return circularbuffer_entry_;
181 }
182
183 public:
184
188 CircularBufferIterator() = default;
189
194 CircularBufferIterator(const CircularBufferIterator<false> & iter) :
195 attached_circularbuffer_(iter.attached_circularbuffer_),
196 circularbuffer_entry_(iter.circularbuffer_entry_),
197 window_idx_(iter.window_idx_)
198 {}
199
204 CircularBufferIterator(const CircularBufferIterator<true> & iter) :
205 attached_circularbuffer_(iter.attached_circularbuffer_),
206 circularbuffer_entry_(iter.circularbuffer_entry_),
207 window_idx_(iter.window_idx_)
208 {}
209
215 CircularBufferIterator& operator=(const CircularBufferIterator& rhs) = default;
216
218 bool operator<(const CircularBufferIterator& rhs) const
219 {
220 sparta_assert(attached_circularbuffer_ == rhs.attached_circularbuffer_,
221 "Cannot compare CircularBufferIterators created by different CircularBuffers.");
222 return window_idx_ > rhs.window_idx_;
223 }
224
226 bool operator>(const CircularBufferIterator& rhs) const
227 {
228 sparta_assert(attached_circularbuffer_ == rhs.attached_circularbuffer_,
229 "Cannot compare CircularBufferIterators created by different CircularBuffers.");
230 return window_idx_ < rhs.window_idx_;
231 }
232
234 bool operator==(const CircularBufferIterator& rhs) const
235 {
236 sparta_assert(attached_circularbuffer_ == rhs.attached_circularbuffer_,
237 "Cannot compare CircularBufferIterators created by different CircularBuffers.");
238 return (window_idx_ == rhs.window_idx_);
239 }
240
242 bool operator!=(const CircularBufferIterator& rhs) const
243 {
244 sparta_assert(attached_circularbuffer_ == rhs.attached_circularbuffer_,
245 "Cannot compare CircularBufferIterators created by different CircularBuffers.");
246 return !operator==(rhs);
247 }
248
251 bool isValid() const
252 {
253 if(attached_circularbuffer_) {
254 return attached_circularbuffer_->isValidIterator_(window_idx_);
255 }
256 return false;
257 }
258
260 DataReferenceType operator* () {
261 sparta_assert(attached_circularbuffer_,
262 "This iterator is not attached to a CircularBuffer. Was it initialized?");
263 sparta_assert(isValid(), "Iterator is not valid for dereferencing");
264 return circularbuffer_entry_->data;
265 }
266 value_type* operator->()
267 {
268 sparta_assert(attached_circularbuffer_,
269 "This iterator is not attached to a CircularBuffer. Was it initialized?");
270 sparta_assert(isValid(), "Iterator is not valid for dereferencing");
271 return &circularbuffer_entry_->data;
272 }
273 const value_type* operator->() const
274 {
275 sparta_assert(attached_circularbuffer_,
276 "This iterator is not attached to a CircularBuffer. Was it initialized?");
277 sparta_assert(isValid(), "Iterator is not valid for dereferencing");
278 return &circularbuffer_entry_->data;
279 }
280
283 CircularBufferIterator & operator++() {
284 sparta_assert(attached_circularbuffer_,
285 "This iterator is not attached to a CircularBuffer. Was it initialized?");
286 if(isValid()) {
287 ++window_idx_;
288 ++circularbuffer_entry_;
289 }
290 else {
291 sparta_assert(!"Attempt to increment an invalid iterator");
292 }
293 return *this;
294 }
295
297 CircularBufferIterator operator++ (int) {
298 CircularBufferIterator buf_iter(*this);
299 operator++();
300 return buf_iter;
301 }
302
304 CircularBufferIterator & operator-- ()
305 {
306 sparta_assert(attached_circularbuffer_, "The iterator is not attached to a CircularBuffer. Was it initialized?");
307 if(attached_circularbuffer_->isValidIterator_(window_idx_ - 1)) {
308 --window_idx_;
309 --circularbuffer_entry_;
310 }
311 else {
312 sparta_assert(!"Attempt to decrement an iterator beyond bounds or that is invalid");
313 }
314 return *this;
315 }
316
318 CircularBufferIterator operator-- (int) {
319 CircularBufferIterator buf_iter(*this);
320 operator--();
321 return buf_iter;
322 }
323
328 friend class CircularBufferIterator<true>;
329 };
330
331 /*
332 * Custom class for reverse iterator to support validity checking
333 */
334 template <typename iter_type>
335 class CircularBufferReverseIterator : public std::reverse_iterator<iter_type>
336 {
337 public:
338 typedef typename std::conditional<iter_type::is_const_iterator_type,
339 InternalCircularBufferConstIterator,
340 InternalCircularBufferIterator>::type SpecificCircularBufferIterator;
341 SpecificCircularBufferIterator circularbuffer_entry_;
342
343 explicit CircularBufferReverseIterator(const iter_type & it) :
344 std::reverse_iterator<iter_type>(it)
345 {}
346
347 template<class U >
348 CircularBufferReverseIterator(const std::reverse_iterator<U> & other) :
349 std::reverse_iterator<iter_type>(other)
350 {}
351
352 bool isValid() const {
353 auto it = std::reverse_iterator<iter_type>::base();
354 try {
355 return (--it).isValid();
356 }
357 catch(...) {
358 return false;
359 }
360 return false;
361 }
362 private:
363 friend class CircularBuffer<value_type>;
364 SpecificCircularBufferIterator getInternalBufferEntry_() const {
365 auto it = std::reverse_iterator<iter_type>::base();
366 return (--it).circularbuffer_entry_;
367 }
368 };
369
370 public:
371
373 typedef CircularBufferIterator<false> iterator;
374
376 typedef CircularBufferIterator<true> const_iterator;
377
379 typedef CircularBufferReverseIterator<const_iterator> const_reverse_iterator;
380
382 typedef CircularBufferReverseIterator<iterator> reverse_iterator;
383
420 CircularBuffer(const std::string & name,
421 const uint32_t max_size,
422 const Clock * clk,
423 StatisticSet * statset = nullptr,
428
431
434
444 void enableCollection(TreeNode * parent) {
445 collector_.
447 this, capacity()));
448 }
449
451 std::string getName() const {
452 return name_;
453 }
454
461 bool isValid(uint32_t idx) const {
462 return idx < size();
463 }
464
469 size_type capacity() const {
470 return max_size_;
471 }
472
477 size_type size() const {
478 return num_valid_;
479 }
480
487 size_type numFree() const {
488 return capacity() - size();
489 }
490
500 void push_back(const value_type& dat)
501 {
502 push_backImpl_(dat);
503 }
504
514 void push_back(value_type&& dat)
515 {
516 push_backImpl_(std::move(dat));
517 }
518
524 iterator insert(const iterator & entry, const value_type& dat)
525 {
526 return insertEntry_(entry, dat);
527 }
528
534 iterator insert(const iterator & entry, value_type&& dat)
535 {
536 return insertEntry_(entry, std::move(dat));
537 }
538
544 iterator insert(const const_iterator & entry, const value_type& dat)
545 {
546 return insertEntry_(entry, dat);
547 }
548
554 iterator insert(const const_iterator & entry, value_type&& dat)
555 {
556 return insertEntry_(entry, std::move(dat));
557 }
558
564 {
565 sparta_assert(entry.attached_circularbuffer_ == this,
566 "Cannot erase an entry created by another CircularBuffer");
567 eraseEntry_(entry);
568 }
569
575 {
576 sparta_assert(entry.base().attached_circularbuffer_ == this,
577 "Cannot erase an entry created by another CircularBuffer");
578 eraseEntry_(entry);
579 }
580 void erase(reverse_iterator entry) {
582 }
583
588 void clear()
589 {
590 circularbuffer_data_.clear();
591 num_valid_ = 0;
592 start_idx_ = end_idx_;
593 updateUtilizationCounters_();
594 }
595
603 if(size()) {
604 return iterator(this,
605 circularbuffer_data_.begin(),
606 circularbuffer_data_.begin()->window_idx);
607 }
608 return end();
609 }
610
616 return iterator(this, circularbuffer_data_.end(), end_idx_);
617 }
618
626 if(size()) {
627 return const_iterator(this,
628 circularbuffer_data_.begin(),
629 circularbuffer_data_.begin()->window_idx);
630 }
631 return end();
632 }
633
639 return const_iterator(this, circularbuffer_data_.end(), end_idx_);
640 }
641
651
657
667
673
681 value_type operator[](const uint32_t idx) {
682 sparta_assert(idx < size(), "Index out of range");
683 auto it = this->begin();
684 std::advance(it, idx);
685 return *it;
686 }
687
688 private:
689
690 // Used by the internal iterator type to see if it's still
691 // valid
692 bool isValidIterator_(uint32_t window_idx) const {
693 return (window_idx >= start_idx_ && window_idx < end_idx_);
694 }
695
696 template<typename EntryIteratorT>
697 void eraseEntry_(const EntryIteratorT & entry)
698 {
699 sparta_assert(entry.isValid());
700 circularbuffer_data_.erase(entry.getInternalBufferEntry_());
701 --num_valid_;
702 invalidateIndexes_();
703 }
704
705 template<typename U>
706 void push_backImpl_(U&& dat)
707 {
708 circularbuffer_data_.emplace(circularbuffer_data_.end(), std::forward<U>(dat), end_idx_);
709 ++end_idx_;
710 if(num_valid_ == max_size_) {
711 circularbuffer_data_.pop_front();
712 ++start_idx_;
713 }
714 else {
715 ++num_valid_;
716 }
717
718 // XXX Remove when testing complete. This is an O(n) operation
719 sparta_assert(circularbuffer_data_.size() <= size());
720
721 updateUtilizationCounters_();
722 }
723
724 template<typename EntryIteratorT, typename U>
725 iterator insertEntry_(const EntryIteratorT & entry, U&& dat)
726 {
727 // If the buffer is empty, the iterator is not valid, so
728 // just do a push_back
729 if(circularbuffer_data_.empty()) {
730 push_back(std::forward<U>(dat));
731 return begin();
732 }
733 sparta_assert(entry.isValid(),
734 "Cannot insert into Circularbuffer at given iterator");
735 auto it = circularbuffer_data_.insert(entry.getInternalBufferEntry_(),
736 CircularBufferData(std::forward<U>(dat), end_idx_));
737 ++num_valid_;
738 invalidateIndexes_();
739 return iterator(this, it, it->window_idx);
740 }
741
742 void invalidateIndexes_() {
743 // To invalidate any and all outstanding iterators, move
744 // the start/end indexes outside the current window. Do
745 // not set the start_idx_ to the old end_idx_ as any older
746 // "end" iterator would be considered valid (equals the
747 // start_idx_)
748 start_idx_ = ++end_idx_;
749 for(auto & it : circularbuffer_data_) {
750 it.window_idx = end_idx_;
751 ++end_idx_;
752 }
753 }
754
755 void updateUtilizationCounters_() {
756 // Update Counters
757 if(!utilization_count_.empty()) {
758 if(previous_valid_entry_ != num_valid_) {
759 utilization_count_[previous_valid_entry_]->stopCounting();
760 utilization_count_[num_valid_]->startCounting();
761 previous_valid_entry_ = num_valid_;
762 }
763 if (num_valid_ > utilization_max_->get()) {
764 utilization_max_->set(num_valid_);
765 }
766 }
767 }
768
769 const size_type max_size_;
770 const std::string name_;
771 CircularBufferDataStorage circularbuffer_data_;
773 size_type num_valid_ = 0;
774 uint64_t start_idx_ = 0;
776 uint64_t end_idx_ = 0;
778
780 // Counters
781 std::vector<std::unique_ptr<sparta::CycleCounter>> utilization_count_;
782 std::unique_ptr<StatisticDef> weighted_utilization_avg_;
783 std::unique_ptr<StatisticInstance> avg_instance_;
784 std::unique_ptr<sparta::Counter> utilization_max_;
785
786 // The last valid entry (to close out a utilization)
787 uint32_t previous_valid_entry_ = 0;
788
790 // Collectors
791 std::unique_ptr<collection::IterableCollector<CircularBuffer<value_type> > > collector_;
792 };
793
795 // Definitions...
796 template<class DataT>
798 uint32_t max_size,
799 const Clock * clk,
800 StatisticSet * statset,
801 InstrumentationNode::visibility_t stat_vis_general,
802 InstrumentationNode::visibility_t stat_vis_detailed,
805 max_size_(max_size),
806 name_(name)
807 {
808 if(statset)
809 {
810 std::stringstream expression_numerator;
811 std::stringstream expression_denum;
812 expression_denum << "( ";
813 expression_numerator << "( ";
814 // The size is 0 entries up to max_size
815 for(uint32_t i = 0; i < max_size_ + 1; ++i)
816 {
817 std::stringstream str;
818 str << name << "_util_cnt" << i;
819
820 InstrumentationNode::visibility_t visibility = stat_vis_detailed;
821
822 if (i == 0 || i == (max_size_)) {
823 if(stat_vis_general == InstrumentationNode::AUTO_VISIBILITY)
824 {
826 }
827 else{
828 visibility = stat_vis_general;
829 }
830 }
831 else // we are internal counts for different levels of utilization.
832 {
833 if(stat_vis_detailed == InstrumentationNode::AUTO_VISIBILITY)
834 {
836 }
837 else{
838 visibility = stat_vis_detailed;
839 }
840 }
841
842 utilization_count_.
843 emplace_back(new CycleCounter(statset,
844 str.str(),
845 name + "_utilization_count", i,
846 "Entry Utilization Counts of " + name,
847 CounterBase::COUNT_NORMAL, clk, visibility));
848 expression_numerator << "( "<< i << " * " << name << "_util_cnt" << i << " )";
849 expression_denum << name << "_util_cnt" << i;
850 if( i != max_size_ )
851 {
852 expression_numerator << " + ";
853 expression_denum << " + ";
854 }
855
856 }
857 expression_numerator << " )";
858 expression_denum << " )";
859 utilization_count_[0]->startCounting();
860
861 if(stat_vis_avg == InstrumentationNode::AUTO_VISIBILITY)
862 {
864 }
865 // add a statasticdef to the set for the weighted average expression_numerator.str() + "/" + expression_denum.str()
866 weighted_utilization_avg_.reset(new StatisticDef(statset,
867 name + "_utilization_weighted_avg",
868 "Calculate the weighted average of the CircularBuffer's utilization",
869 statset,
870 expression_numerator.str() + " / " + expression_denum.str(),
871 sparta::StatisticDef::VS_ABSOLUTE, stat_vis_avg));
872
873 // Add a counter to track the maximum utilization
874 if(stat_vis_max == InstrumentationNode::AUTO_VISIBILITY)
875 {
877 }
878 utilization_max_.reset(new Counter(statset,
879 name + "_utilization_max",
880 "The maximum utilization",
882 stat_vis_max));
883 }
884 }
885
886
887
888}
Defines a few handy (and now deprecated) C++ iterator traits.
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.
Contains a statistic definition (some useful information which can be computed)
Contains a StatisticInstance which refers to a StatisticDef or Counter and some local state to comput...
A data structure allowing appending at the end, beginning, or insert in the middle,...
std::string getName() const
Get this CircularBuffer's name.
iterator insert(const iterator &entry, value_type &&dat)
Insert the given data before the given iterator.
CircularBuffer(const CircularBuffer< value_type > &)=delete
No copies allowed for CircularBuffer.
const_iterator end() const
Returns an iterator referring to past-the-end of the newest element in the CircularBuffer.
CircularBufferReverseIterator< const_iterator > const_reverse_iterator
Typedef for regular reverse iterator.
const_reverse_iterator rbegin() const
Get the iterator pointing to the oldest entry of CircularBuffer.
void erase(const_reverse_iterator entry)
erase the index at which the entry exists in the CircularBuffer.
reverse_iterator rend()
Returns an iterator referring to past-the-end of the newest element in the CircularBuffer.
iterator end()
Returns an iterator referring to past-the-end of the newest element in the CircularBuffer.
reverse_iterator rbegin()
Get the iterator pointing to the oldest entry of CircularBuffer.
void enableCollection(TreeNode *parent)
Request that this queue begin collecting its contents for pipeline collection.
CircularBuffer(const std::string &name, const uint32_t max_size, const Clock *clk, StatisticSet *statset=nullptr, InstrumentationNode::visibility_t stat_vis_general=InstrumentationNode::AUTO_VISIBILITY, InstrumentationNode::visibility_t stat_vis_detailed=InstrumentationNode::VIS_HIDDEN, InstrumentationNode::visibility_t stat_vis_max=InstrumentationNode::AUTO_VISIBILITY, InstrumentationNode::visibility_t stat_vis_avg=InstrumentationNode::AUTO_VISIBILITY)
Construct a CircularBuffer.
size_type size() const
Return the number of valid entries.
iterator insert(const const_iterator &entry, const value_type &dat)
Insert the given data before the given iterator.
CircularBuffer & operator=(const CircularBuffer< value_type > &)=delete
No copies, no moves allowed for CircularBuffer.
CircularBufferIterator< false > iterator
Typedef for regular iterator.
CircularBufferIterator< true > const_iterator
Typedef for constant iterator.
void push_back(value_type &&dat)
Append data to the end of CircularBuffer, and return a CircularBufferIterator.
CircularBufferReverseIterator< iterator > reverse_iterator
Typedef for constant reverse iterator.
bool isValid(uint32_t idx) const
Determine if data at the index is valid.
const_reverse_iterator rend() const
Returns an iterator referring to past-the-end of the newest element in the CircularBuffer.
iterator begin()
Get the iterator pointing to the oldest entry of CircularBuffer.
void erase(const_iterator entry)
erase the index at which the entry exists in the CircularBuffer.
value_type operator[](const uint32_t idx)
Returns the data at the given index. Will assert if out of range.
void push_back(const value_type &dat)
Append data to the end of CircularBuffer, and return a CircularBufferIterator.
iterator insert(const iterator &entry, const value_type &dat)
Insert the given data before the given iterator.
size_type capacity() const
Return the fixed size of this CircularBuffer.
size_type numFree() const
Return the number of free entries.
const_iterator begin() const
Get the iterator pointing to the oldest entry of CircularBuffer.
void clear()
Empty the contents of the CircularBuffer.
iterator insert(const const_iterator &entry, value_type &&dat)
Insert the given data before the given iterator.
A representation of simulated time.
Definition Clock.hpp:51
@ COUNT_LATEST
Counter holds the latest value (from most recent activity) and can increase or decrease at any time.
@ COUNT_NORMAL
Counter counts the number of times something happens like one would expect. This is a weakly monotoni...
Represents a counter of type counter_type (uint64_t). 2 and greater than 0 with a ceiling specified....
Definition Counter.hpp:27
Represents a cycle counter.
static constexpr visibility_t CONTAINER_DEFAULT_VISIBILITY
the actual visibility that the sparta containers such as buffer, queue, and array will use when VIS_S...
uint32_t visibility_t
Continuous visibility level. Several key points along continum are indicated within Visibility.
static constexpr visibility_t DEFAULT_VISIBILITY
Default node visibility.
@ VIS_HIDDEN
Hidden hint. Lowest possible visibility.
static constexpr visibility_t AUTO_VISIBILITY
The default sparta resource visibility value that should be used. This is an alias of VIS_MAX at the ...
Contains a statistic definition (some useful information which can be computed)
@ VS_ABSOLUTE
An absolute number having no units (typical default)
Set of StatisticDef and CounterBase-derived objects for visiblility through a sparta Tree.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
A collector of any iterable type (std::vector, std::list, sparta::Buffer, etc)
Macros for handling exponential backoff.