diff options
author | 2024-01-19 14:33:20 -0800 | |
---|---|---|
committer | 2024-10-31 11:55:22 -0700 | |
commit | 0a6dc40104010537d473ac8f613c51545e214ebc (patch) | |
tree | 9f1586f968219e4a1ec7ef4895dcfaadc2f4d913 | |
parent | fc8ace08bb2b9f764b9e56fb105609fc20421f4e (diff) |
A uint64 array wrapper for optimized allocation and copying
Bug: 315052795
Bug: 357697495
Test: atest libbattery_test; atest FrameworksCoreTests
Flag: EXEMPT bugfix
Change-Id: I3c09c438131b3f67ef04436667e589d1d86aff71
-rw-r--r-- | libs/battery/LongArrayMultiStateCounter.cpp | 144 | ||||
-rw-r--r-- | libs/battery/LongArrayMultiStateCounter.h | 61 | ||||
-rw-r--r-- | libs/battery/LongArrayMultiStateCounterTest.cpp | 28 | ||||
-rw-r--r-- | libs/battery/MultiStateCounter.h | 91 | ||||
-rw-r--r-- | libs/battery/MultiStateCounterTest.cpp | 7 |
5 files changed, 231 insertions, 100 deletions
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp index 125cfaffa4..35c40ab7e6 100644 --- a/libs/battery/LongArrayMultiStateCounter.cpp +++ b/libs/battery/LongArrayMultiStateCounter.cpp @@ -21,58 +21,134 @@ namespace android { namespace battery { -template <> -bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue, - const std::vector<uint64_t>& newValue, - std::vector<uint64_t>* outValue) const { - size_t size = previousValue.size(); - if (newValue.size() != size) { - ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size); - return false; +Uint64ArrayRW::Uint64ArrayRW(const Uint64Array ©) : Uint64Array(copy.size()) { + if (mSize != 0 && copy.data() != nullptr) { + mData = new uint64_t[mSize]; + memcpy(mData, copy.data(), mSize * sizeof(uint64_t)); + } else { + mData = nullptr; } +} - bool is_delta_valid = true; - for (int i = size - 1; i >= 0; i--) { - if (newValue[i] >= previousValue[i]) { - (*outValue)[i] = newValue[i] - previousValue[i]; +uint64_t *Uint64ArrayRW::dataRW() { + if (mData == nullptr) { + mData = new uint64_t[mSize]; + memset(mData, 0, mSize * sizeof(uint64_t)); + } + return mData; +} + +Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) { + if (t.size() != mSize) { + delete[] mData; + mSize = t.size(); + mData = nullptr; + } + if (mSize != 0) { + if (t.data() != nullptr) { + mData = new uint64_t[mSize]; + memcpy(mData, t.data(), mSize * sizeof(uint64_t)); } else { - (*outValue)[i] = 0; - is_delta_valid = false; + mData = nullptr; } } - return is_delta_valid; + return *this; +} + +std::ostream &operator<<(std::ostream &os, const Uint64Array &v) { + os << "{"; + const uint64_t *data = v.data(); + if (data != nullptr) { + bool first = true; + for (size_t i = 0; i < v.size(); i++) { + if (!first) { + os << ", "; + } + os << data[i]; + first = false; + } + } + os << "}"; + return os; +} + +// Convenience constructor for tests +Uint64ArrayRW::Uint64ArrayRW(std::initializer_list<uint64_t> init) : Uint64Array(init.size()) { + mData = new uint64_t[mSize]; + memcpy(mData, init.begin(), mSize * sizeof(uint64_t)); +} + +// Used in tests only. +bool Uint64Array::operator==(const Uint64Array &other) const { + if (size() != other.size()) { + return false; + } + const uint64_t* thisData = data(); + const uint64_t* thatData = other.data(); + for (size_t i = 0; i < mSize; i++) { + const uint64_t v1 = thisData != nullptr ? thisData[i] : 0; + const uint64_t v2 = thatData != nullptr ? thatData[i] : 0; + if (v1 != v2) { + return false; + } + } + return true; } template <> -void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1, - const std::vector<uint64_t>& value2, const uint64_t numerator, - const uint64_t denominator) const { +void LongArrayMultiStateCounter::add(Uint64ArrayRW *value1, const Uint64Array &value2, + const uint64_t numerator, const uint64_t denominator) const { + const uint64_t* data2 = value2.data(); + if (data2 == nullptr) { + return; + } + + uint64_t* data1 = value1->dataRW(); + size_t size = value2.size(); if (numerator != denominator) { - for (int i = value2.size() - 1; i >= 0; i--) { + for (size_t i = 0; i < size; i++) { // The caller ensures that denominator != 0 - (*value1)[i] += value2[i] * numerator / denominator; + data1[i] += data2[i] * numerator / denominator; } } else { - for (int i = value2.size() - 1; i >= 0; i--) { - (*value1)[i] += value2[i]; + for (size_t i = 0; i < size; i++) { + data1[i] += data2[i]; } } } -template <> -std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const { - std::stringstream s; - s << "{"; - bool first = true; - for (uint64_t n : v) { - if (!first) { - s << ", "; +template<> +bool LongArrayMultiStateCounter::delta(const Uint64ArrayRW &previousValue, + const Uint64Array &newValue, Uint64ArrayRW *outValue) const { + size_t size = previousValue.size(); + if (newValue.size() != size) { + ALOGE("Incorrect array size: %d, should be %d", (int) newValue.size(), (int) size); + return false; + } + if (outValue->size() != size) { + ALOGE("Incorrect outValue size: %d, should be %d", (int) outValue->size(), (int) size); + return false; + } + + bool is_delta_valid = true; + const uint64_t *prevData = previousValue.data(); + const uint64_t *newData = newValue.data(); + uint64_t *outData = outValue->dataRW(); + for (size_t i = 0; i < size; i++) { + if (prevData == nullptr) { + if (newData == nullptr) { + outData[i] = 0; + } else { + outData[i] = newData[i]; + } + } else if (newData == nullptr || newData[i] < prevData[i]) { + outData[i] = 0; + is_delta_valid = false; + } else { + outData[i] = newData[i] - prevData[i]; } - s << n; - first = false; } - s << "}"; - return s.str(); + return is_delta_valid; } } // namespace battery diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h index f3439f6a0c..e00c96898e 100644 --- a/libs/battery/LongArrayMultiStateCounter.h +++ b/libs/battery/LongArrayMultiStateCounter.h @@ -23,7 +23,66 @@ namespace android { namespace battery { -typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter; +/** + * Wrapper for an array of uint64's. + */ +class Uint64Array { + protected: + size_t mSize; + + public: + Uint64Array() : Uint64Array(0) {} + + Uint64Array(size_t size) : mSize(size) {} + + virtual ~Uint64Array() {} + + size_t size() const { return mSize; } + + /** + * Returns the wrapped array. + * + * Nullable! Null should be interpreted the same as an array of zeros + */ + virtual const uint64_t *data() const { return nullptr; } + + friend std::ostream &operator<<(std::ostream &os, const Uint64Array &v); + + // Test API + bool operator==(const Uint64Array &other) const; +}; + +/** + * Mutable version of Uint64Array. + */ +class Uint64ArrayRW: public Uint64Array { + uint64_t* mData; + +public: + Uint64ArrayRW() : Uint64ArrayRW(0) {} + + Uint64ArrayRW(size_t size) : Uint64Array(size), mData(nullptr) {} + + Uint64ArrayRW(const Uint64Array ©); + + // Need an explicit copy constructor. In the initialization context C++ does not understand that + // a Uint64ArrayRW is a Uint64Array. + Uint64ArrayRW(const Uint64ArrayRW ©) : Uint64ArrayRW((const Uint64Array &) copy) {} + + // Test API + Uint64ArrayRW(std::initializer_list<uint64_t> init); + + ~Uint64ArrayRW() override { delete[] mData; } + + const uint64_t *data() const override { return mData; } + + // NonNull. Will initialize the wrapped array if it is null. + uint64_t *dataRW(); + + Uint64ArrayRW &operator=(const Uint64Array &t); +}; + +typedef MultiStateCounter<Uint64ArrayRW, Uint64Array> LongArrayMultiStateCounter; } // namespace battery } // namespace android diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp index e4e6b2a49f..1c74e3fd14 100644 --- a/libs/battery/LongArrayMultiStateCounterTest.cpp +++ b/libs/battery/LongArrayMultiStateCounterTest.cpp @@ -24,25 +24,25 @@ namespace battery { class LongArrayMultiStateCounterTest : public testing::Test {}; TEST_F(LongArrayMultiStateCounterTest, stateChange) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); // Time was split in half between the two states, so the counts will be split 50:50 too - EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0)); - EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, accumulation) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); testCounter.setState(0, 4000); - testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000); + testCounter.updateValue(Uint64ArrayRW({200, 300, 400, 500}), 8000); // The first delta is split 50:50: // 0: {50, 100, 150, 200} @@ -50,16 +50,16 @@ TEST_F(LongArrayMultiStateCounterTest, accumulation) { // The second delta is split 4:1 // 0: {80, 80, 80, 80} // 1: {20, 20, 20, 20} - EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0)); - EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({130, 180, 230, 280}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({70, 120, 170, 220}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, toString) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1", testCounter.toString().c_str()); diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h index 04b718698e..fadc4ffd41 100644 --- a/libs/battery/MultiStateCounter.h +++ b/libs/battery/MultiStateCounter.h @@ -35,12 +35,12 @@ namespace battery { typedef uint16_t state_t; -template <class T> +template <class T, class V> class MultiStateCounter { - uint16_t stateCount; + const uint16_t stateCount; + const V emptyValue; state_t currentState; time_t lastStateChangeTimestamp; - T emptyValue; T lastValue; time_t lastUpdateTimestamp; T deltaValue; @@ -54,7 +54,7 @@ class MultiStateCounter { State* states; public: - MultiStateCounter(uint16_t stateCount, const T& emptyValue); + MultiStateCounter(uint16_t stateCount, const V& emptyValue); virtual ~MultiStateCounter(); @@ -66,35 +66,35 @@ public: * Copies the current state and accumulated times-in-state from the source. Resets * the accumulated value. */ - void copyStatesFrom(const MultiStateCounter<T>& source); + void copyStatesFrom(const MultiStateCounter<T, V> &source); - void setValue(state_t state, const T& value); + void setValue(state_t state, const V& value); /** * Updates the value by distributing the delta from the previously set value * among states according to their respective time-in-state. * Returns the delta from the previously set value. */ - const T& updateValue(const T& value, time_t timestamp); + const V& updateValue(const V& value, time_t timestamp); /** * Updates the value by distributing the specified increment among states according * to their respective time-in-state. */ - void incrementValue(const T& increment, time_t timestamp); + void incrementValue(const V& increment, time_t timestamp); /** * Adds the specified increment to the value for the current state, without affecting * the last updated value or timestamp. Ignores partial time-in-state: the entirety of * the increment is given to the current state. */ - void addValue(const T& increment); + void addValue(const V& increment); void reset(); uint16_t getStateCount(); - const T& getCount(state_t state); + const V& getCount(state_t state); std::string toString(); @@ -104,27 +104,25 @@ private: * Returns true iff the combination of previousValue and newValue is valid * (newValue >= prevValue) */ - bool delta(const T& previousValue, const T& newValue, T* outValue) const; + bool delta(const T& previousValue, const V& newValue, T* outValue) const; /** * Adds value2 to value1 and stores the result in value1. Denominator is * guaranteed to be non-zero. */ - void add(T* value1, const T& value2, const uint64_t numerator, + void add(T* value1, const V& value2, const uint64_t numerator, const uint64_t denominator) const; - - std::string valueToString(const T& value) const; }; // ---------------------- MultiStateCounter Implementation ------------------------- // Since MultiStateCounter is a template, the implementation must be inlined. -template <class T> -MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue) +template <class T, class V> +MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue) : stateCount(stateCount), + emptyValue(emptyValue), currentState(0), lastStateChangeTimestamp(-1), - emptyValue(emptyValue), lastValue(emptyValue), lastUpdateTimestamp(-1), deltaValue(emptyValue), @@ -136,13 +134,13 @@ MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue } } -template <class T> -MultiStateCounter<T>::~MultiStateCounter() { +template <class T, class V> +MultiStateCounter<T, V>::~MultiStateCounter() { delete[] states; }; -template <class T> -void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) { if (enabled == isEnabled) { return; } @@ -167,8 +165,8 @@ void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) { } } -template <class T> -void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) { if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) { // If the update arrived out-of-order, just push back the timestamp to // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate @@ -198,8 +196,8 @@ void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { lastStateChangeTimestamp = timestamp; } -template <class T> -void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) { +template <class T, class V> +void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) { if (stateCount != source.stateCount) { ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount); return; @@ -214,14 +212,14 @@ void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) { lastUpdateTimestamp = source.lastUpdateTimestamp; } -template <class T> -void MultiStateCounter<T>::setValue(state_t state, const T& value) { +template <class T, class V> +void MultiStateCounter<T, V>::setValue(state_t state, const V& value) { states[state].counter = value; } -template <class T> -const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { - T* returnValue = &emptyValue; +template <class T, class V> +const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) { + const V* returnValue = &emptyValue; // If the counter is disabled, we ignore the update, except when the counter got disabled after // the previous update, in which case we still need to pick up the residual delta. @@ -250,8 +248,8 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { } } else { std::stringstream str; - str << "updateValue is called with a value " << valueToString(value) - << ", which is lower than the previous value " << valueToString(lastValue) + str << "updateValue is called with a value " << value + << ", which is lower than the previous value " << lastValue << "\n"; ALOGE("%s", str.str().c_str()); @@ -276,23 +274,25 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { return *returnValue; } -template <class T> -void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) { +// T newValue; +// newValue = lastValue; // Copy assignment, not initialization. T newValue = lastValue; add(&newValue, increment, 1 /* numerator */, 1 /* denominator */); updateValue(newValue, timestamp); } -template <class T> -void MultiStateCounter<T>::addValue(const T& value) { +template <class T, class V> +void MultiStateCounter<T, V>::addValue(const V& value) { if (!isEnabled) { return; } add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */); } -template <class T> -void MultiStateCounter<T>::reset() { +template <class T, class V> +void MultiStateCounter<T, V>::reset() { lastStateChangeTimestamp = -1; lastUpdateTimestamp = -1; for (int i = 0; i < stateCount; i++) { @@ -301,25 +301,26 @@ void MultiStateCounter<T>::reset() { } } -template <class T> -uint16_t MultiStateCounter<T>::getStateCount() { +template <class T, class V> +uint16_t MultiStateCounter<T, V>::getStateCount() { return stateCount; } -template <class T> -const T& MultiStateCounter<T>::getCount(state_t state) { +template <class T, class V> +const V& MultiStateCounter<T, V>::getCount(state_t state) { return states[state].counter; } -template <class T> -std::string MultiStateCounter<T>::toString() { +template <class T, class V> +std::string MultiStateCounter<T, V>::toString() { std::stringstream str; +// str << "LAST VALUE: " << valueToString(lastValue); str << "["; for (int i = 0; i < stateCount; i++) { if (i != 0) { str << ", "; } - str << i << ": " << valueToString(states[i].counter); + str << i << ": " << states[i].counter; if (states[i].timeInStateSinceUpdate > 0) { str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate; } diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp index a51a38a6c7..589b7fe4e3 100644 --- a/libs/battery/MultiStateCounterTest.cpp +++ b/libs/battery/MultiStateCounterTest.cpp @@ -21,7 +21,7 @@ namespace android { namespace battery { -typedef MultiStateCounter<double> DoubleMultiStateCounter; +typedef MultiStateCounter<double, double> DoubleMultiStateCounter; template <> bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue, @@ -41,11 +41,6 @@ void DoubleMultiStateCounter::add(double* value1, const double& value2, const ui } } -template <> -std::string DoubleMultiStateCounter::valueToString(const double& v) const { - return std::to_string(v); -} - class MultiStateCounterTest : public testing::Test {}; TEST_F(MultiStateCounterTest, constructor) { |