From 50a533f10134e7bc377dfdb9209b0334fd01cd24 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 13 Apr 2019 11:02:43 +0200 Subject: [PATCH] base: Add operators to SatCounter Add shift, add and subtract assignment operators, as well as copy and move constructor and assignments to SatCounter, so that it they can be used by the prefetchers. Also add extra useful functions to calculate saturation oercentile so that the instantiator does not need to be aware of the counter's maximum value. Change-Id: I61d0cb28c8375b9d2774a39011e4a0aa6fe9ccb7 Signed-off-by: Daniel Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/17996 Tested-by: kokoro Reviewed-by: Jason Lowe-Power Maintainer: Jason Lowe-Power --- src/base/sat_counter.hh | 114 +++++++++++++++++++++++ src/base/sat_counter.test.cc | 173 +++++++++++++++++++++++++++++++++++ 2 files changed, 287 insertions(+) diff --git a/src/base/sat_counter.hh b/src/base/sat_counter.hh index 342a3382f..3de9486db 100644 --- a/src/base/sat_counter.hh +++ b/src/base/sat_counter.hh @@ -56,6 +56,9 @@ class SatCounter { public: + /** The default constructor should never be used. */ + SatCounter() = delete; + /** * Constructor for the counter. The explicit keyword is used to make * sure the user does not assign a number to the counter thinking it @@ -75,6 +78,58 @@ class SatCounter "Saturating counter's Initial value exceeds max value."); } + /** Copy constructor. */ + SatCounter(const SatCounter& other) + : initialVal(other.initialVal), maxVal(other.maxVal), + counter(other.counter) + { + } + + /** Copy assignment. */ + SatCounter& operator=(const SatCounter& other) { + if (this != &other) { + SatCounter temp(other); + this->swap(temp); + } + return *this; + } + + /** Move constructor. */ + SatCounter(SatCounter&& other) + { + initialVal = other.initialVal; + maxVal = other.maxVal; + counter = other.counter; + SatCounter temp(0); + other.swap(temp); + } + + /** Move assignment. */ + SatCounter& operator=(SatCounter&& other) { + if (this != &other) { + initialVal = other.initialVal; + maxVal = other.maxVal; + counter = other.counter; + SatCounter temp(0); + other.swap(temp); + } + return *this; + } + + /** + * Swap the contents of every member of the class. Used for the default + * copy-assignment created by the compiler. + * + * @param other The other object to swap contents with. + */ + void + swap(SatCounter& other) + { + std::swap(initialVal, other.initialVal); + std::swap(maxVal, other.maxVal); + std::swap(counter, other.counter); + } + /** Pre-increment operator. */ SatCounter& operator++() @@ -113,6 +168,49 @@ class SatCounter return old_counter; } + /** Shift-right-assignment. */ + SatCounter& + operator>>=(const int& shift) + { + this->counter >>= shift; + return *this; + } + + /** Shift-left-assignment. */ + SatCounter& + operator<<=(const int& shift) + { + this->counter <<= shift; + if (this->counter > maxVal) { + this->counter = maxVal; + } + return *this; + } + + /** Add-assignment. */ + SatCounter& + operator+=(const int& value) + { + if (maxVal - this->counter >= value) { + this->counter += value; + } else { + this->counter = maxVal; + } + return *this; + } + + /** Subtract-assignment. */ + SatCounter& + operator-=(const int& value) + { + if (this->counter > value) { + this->counter -= value; + } else { + this->counter = 0; + } + return *this; + } + /** * Read the counter's value. */ @@ -121,6 +219,22 @@ class SatCounter /** Reset the counter to its initial value. */ void reset() { counter = initialVal; } + /** + * Calculate saturation percentile of the current counter's value + * with regard to its maximum possible value. + * + * @return A value between 0.0 and 1.0 to indicate which percentile of + * the maximum value the current value is. + */ + double calcSaturation() const { return (double) counter / maxVal; } + + /** + * Whether the counter has achieved its maximum value or not. + * + * @return True if the counter saturated. + */ + bool isSaturated() const { return counter == maxVal; } + private: uint8_t initialVal; uint8_t maxVal; diff --git a/src/base/sat_counter.test.cc b/src/base/sat_counter.test.cc index efc79e034..dbdaf0ae6 100644 --- a/src/base/sat_counter.test.cc +++ b/src/base/sat_counter.test.cc @@ -30,6 +30,8 @@ #include +#include + #include "base/sat_counter.hh" /** @@ -77,6 +79,23 @@ TEST(SatCounterTest, InitialValue) ASSERT_EQ(counter, initial_value); } +/** + * Test calculating saturation percentile. + */ +TEST(SatCounterTest, SaturationPercentile) +{ + const unsigned bits = 3; + const unsigned max_value = (1 << bits) - 1; + SatCounter counter(bits); + + ASSERT_FALSE(counter.isSaturated()); + for (double value = 0.0; value <= max_value; value++, counter++) { + const double saturation = value / max_value; + ASSERT_DOUBLE_EQ(counter.calcSaturation(), saturation); + } + ASSERT_TRUE(counter.isSaturated()); +} + /** * Test back and forth against an int. */ @@ -101,6 +120,55 @@ TEST(SatCounterTest, IntComparison) ASSERT_EQ(counter, 0); } +/** + * Test shift operators. + */ +TEST(SatCounterTest, Shift) +{ + const unsigned bits = 3; + const unsigned max_value = (1 << bits) - 1; + const unsigned initial_value = 1; + SatCounter counter(bits, initial_value); + SatCounter other(bits, initial_value); + // The saturated shift value is just enough to saturate, since greater + // values could generate undefined behavior + SatCounter saturated_counter(bits, bits); + int value = initial_value; + + // Test random shifts + counter <<= 2; + value <<= 2; + ASSERT_EQ(counter, value); + counter >>= 1; + value >>= 1; + ASSERT_EQ(counter, value); + + // Test saturation + counter <<= bits; + ASSERT_EQ(counter, max_value); + + // Test zeroing + counter >>= bits; + ASSERT_EQ(counter, 0); + + // Test saturation against other saturating counter + counter.reset(); + value = initial_value; + counter <<= other; + value <<= other; + ASSERT_EQ(counter, value); + counter <<= saturated_counter; + value = max_value; + ASSERT_EQ(counter, max_value); + + // Test zeroing against other saturating counter + counter >>= other; + value >>= other; + ASSERT_EQ(counter, value); + counter >>= saturated_counter; + ASSERT_EQ(counter, 0); +} + /** * Test both pre and post operators. */ @@ -129,3 +197,108 @@ TEST(SatCounterTest, PrePostOperators) ASSERT_EQ(counter_pre, 0); ASSERT_EQ(counter_post, 0); } + +/** + * Test copy and move for both constructor and assignment. + */ +TEST(SatCounterTest, CopyMove) +{ + const unsigned bits = 3; + const unsigned max_value = (1 << bits) - 1; + const unsigned initial_value = 1; + SatCounter counter(bits, initial_value); + SatCounter deep_copy(1); + SatCounter counter_copy(2); + + // Increase counter value so that we can check if the inner counter is + // being copied + counter++; + + // Copy counter using both the copy constructor and the copy assignment + SatCounter counter_copy_constructor(counter); + deep_copy = counter_copy = counter; + ASSERT_EQ(counter_copy_constructor, initial_value + 1); + ASSERT_EQ(counter_copy, initial_value + 1); + ASSERT_EQ(deep_copy, initial_value + 1); + + // Make sure max value is the same for all of them, and that modifying + // the copies does not modify the original + for (int i = 0; i < 2*max_value; i++) { + counter_copy_constructor++; + counter_copy++; + deep_copy++; + } + ASSERT_EQ(counter, initial_value + 1); + ASSERT_EQ(counter_copy_constructor, max_value); + ASSERT_EQ(counter_copy, max_value); + ASSERT_EQ(deep_copy, max_value); + + // Make sure initial value is the same for all of them + counter_copy_constructor.reset(); + counter_copy.reset(); + deep_copy.reset(); + ASSERT_EQ(counter_copy_constructor, initial_value); + ASSERT_EQ(counter_copy, initial_value); + ASSERT_EQ(deep_copy, initial_value); + + // Now check move + SatCounter counter_move_constructor(std::move(counter)); + ASSERT_EQ(counter, 0); + ASSERT_EQ(counter_move_constructor, initial_value + 1); + + SatCounter counter_move(bits); + counter_move = std::move(counter_move_constructor); + ASSERT_EQ(counter_move_constructor, 0); + ASSERT_EQ(counter_move, initial_value + 1); +} + +/** + * Test add-assignment and subtract assignment. + */ +TEST(SatCounterTest, AddSubAssignment) +{ + const unsigned bits = 3; + const unsigned max_value = (1 << bits) - 1; + SatCounter counter(bits); + SatCounter other(bits, 2); + SatCounter saturated_counter(bits, max_value); + int value = 0; + + // Test add-assignment for a few random values and then saturate + counter += 2; + value += 2; + ASSERT_EQ(counter, value); + counter += 3; + value += 3; + ASSERT_EQ(counter, value); + counter += max_value; + value = max_value; + ASSERT_EQ(counter, value); + + // Test subtract-assignment for a few random values until back to zero + counter -= 2; + value -= 2; + ASSERT_EQ(counter, value); + counter -= 3; + value -= 3; + ASSERT_EQ(counter, value); + counter -= max_value; + value = 0; + ASSERT_EQ(counter, value); + + // Test add-assignment of other saturating counter + counter += other; + value += other; + ASSERT_EQ(counter, value); + counter += saturated_counter; + value = max_value; + ASSERT_EQ(counter, saturated_counter); + + // Test subtract-assignment of other saturating counter + counter -= other; + value -= other; + ASSERT_EQ(counter, value); + counter -= saturated_counter; + ASSERT_EQ(counter, 0); +} + -- 2.30.2