From 1b55186ecfbf7c96a7f708357c8432c4821f4b95 Mon Sep 17 00:00:00 2001 From: Anthony Jones Date: Tue, 24 Sep 2013 15:56:55 +1200 Subject: [PATCH] Bug 888084 - Rolling mean for MFBT; r=waldo --- mfbt/RollingMean.h | 109 +++++++++++++++++++++++++++++ mfbt/Vector.h | 2 +- mfbt/common.mozbuild | 1 + mfbt/tests/TestRollingMean.cpp | 121 +++++++++++++++++++++++++++++++++ mfbt/tests/moz.build | 1 + 5 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 mfbt/RollingMean.h create mode 100644 mfbt/tests/TestRollingMean.cpp diff --git a/mfbt/RollingMean.h b/mfbt/RollingMean.h new file mode 100644 index 000000000000..5caee3bc8304 --- /dev/null +++ b/mfbt/RollingMean.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-w idth: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A set abstraction for enumeration values. */ + +#ifndef mozilla_RollingMean_h_ +#define mozilla_RollingMean_h_ + +#include "mozilla/Assertions.h" +#include "mozilla/TypeTraits.h" +#include "mozilla/Vector.h" + +#include +#include +#include + +namespace mozilla { + +/** + * RollingMean calculates a rolling mean of the values it is given. It + * accumulates the total as values are added and removed. The second type + * argument S specifies the type of the total. This may need to be a bigger + * type in order to maintain that the sum of all values in the average doesn't + * exceed the maximum input value. + * + * WARNING: Float types are not supported due to rounding errors. + */ +template +class RollingMean +{ + private: + size_t mInsertIndex; + size_t mMaxValues; + Vector mValues; + S mTotal; + + public: + static_assert(!IsFloatingPoint::value, + "floating-point types are unsupported due to rounding " + "errors"); + + RollingMean(size_t aMaxValues) + : mInsertIndex(0), + mMaxValues(aMaxValues), + mTotal(0) + { + MOZ_ASSERT(aMaxValues > 0); + } + + RollingMean& operator=(RollingMean&& aOther) { + MOZ_ASSERT(this != &aOther, "self-assignment is forbidden"); + this->~RollingMean(); + new(this) RollingMean(aOther.mMaxValues); + mInsertIndex = aOther.mInsertIndex; + mTotal = aOther.mTotal; + mValues.swap(aOther.mValues); + return *this; + } + + /** + * Insert a value into the rolling mean. + */ + bool insert(T aValue) { + MOZ_ASSERT(mValues.length() <= mMaxValues); + + if (mValues.length() == mMaxValues) { + mTotal = mTotal - mValues[mInsertIndex] + aValue; + mValues[mInsertIndex] = aValue; + } else { + if (!mValues.append(aValue)) + return false; + mTotal = mTotal + aValue; + } + + mInsertIndex = (mInsertIndex + 1) % mMaxValues; + return true; + } + + /** + * Calculate the rolling mean. + */ + T mean() { + MOZ_ASSERT(!empty()); + return T(mTotal / mValues.length()); + } + + bool empty() { + return mValues.empty(); + } + + /** + * Remove all values from the rolling mean. + */ + void clear() { + mValues.clear(); + mInsertIndex = 0; + mTotal = T(0); + } + + size_t maxValues() { + return mMaxValues; + } +}; + +} // namespace mozilla + +#endif // mozilla_RollingMean_h_ diff --git a/mfbt/Vector.h b/mfbt/Vector.h index 1ec19eb6cbdb..eafa6f3aae48 100644 --- a/mfbt/Vector.h +++ b/mfbt/Vector.h @@ -969,7 +969,7 @@ VectorBase::erase(T* it) *it = *(it + 1); ++it; } - popBack(); + popBack(); } template diff --git a/mfbt/common.mozbuild b/mfbt/common.mozbuild index 2fa4e51d2b5e..2e3037fb224b 100644 --- a/mfbt/common.mozbuild +++ b/mfbt/common.mozbuild @@ -48,6 +48,7 @@ mfbt_headers = [ 'RangedPtr.h', 'ReentrancyGuard.h', 'RefPtr.h', + 'RollingMean.h', 'Scoped.h', 'SHA1.h', 'SplayTree.h', diff --git a/mfbt/tests/TestRollingMean.cpp b/mfbt/tests/TestRollingMean.cpp new file mode 100644 index 000000000000..4e8ab2364aaf --- /dev/null +++ b/mfbt/tests/TestRollingMean.cpp @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/RollingMean.h" + +using mozilla::RollingMean; + +class MyClass +{ + public: + uint32_t value; + + MyClass(uint32_t val = 0) : value(val) { + } + + bool operator==(const MyClass& other) const { + return value == other.value; + } + + MyClass operator+(const MyClass& other) const { + return MyClass(value + other.value); + } + + MyClass operator-(const MyClass& other) const { + return MyClass(value - other.value); + } + + MyClass operator/(uint32_t div) const { + return MyClass(value / div); + } +}; + +class RollingMeanSuite +{ + public: + RollingMeanSuite() + { } + + void runTests() { + testZero(); + testClear(); + testRolling(); + testClass(); + testMove(); + } + + private: + void testZero() { + RollingMean mean(3); + MOZ_ASSERT(mean.empty()); + } + + void testClear() { + RollingMean mean(3); + + mean.insert(4); + MOZ_ASSERT(mean.mean() == 4); + + mean.clear(); + MOZ_ASSERT(mean.empty()); + + mean.insert(3); + MOZ_ASSERT(mean.mean() == 3); + } + + void testRolling() { + RollingMean mean(3); + + mean.insert(10); + MOZ_ASSERT(mean.mean() == 10); + + mean.insert(20); + MOZ_ASSERT(mean.mean() == 15); + + mean.insert(35); + MOZ_ASSERT(mean.mean() == 21); + + mean.insert(5); + MOZ_ASSERT(mean.mean() == 20); + + mean.insert(10); + MOZ_ASSERT(mean.mean() == 16); + } + + void testClass() { + RollingMean mean(3); + + mean.insert(MyClass(4)); + MOZ_ASSERT(mean.mean() == MyClass(4)); + + mean.clear(); + MOZ_ASSERT(mean.empty()); + } + + void testMove() { + RollingMean mean(3); + mean = RollingMean(4); + MOZ_ASSERT(mean.maxValues() == 4); + + mean.insert(10); + MOZ_ASSERT(mean.mean() == 10); + + mean = RollingMean(3); + mean.insert(30); + mean.insert(40); + mean.insert(50); + mean.insert(60); + MOZ_ASSERT(mean.mean() == 50); + } + +}; + +int main() +{ + RollingMeanSuite suite; + suite.runTests(); + return 0; +} diff --git a/mfbt/tests/moz.build b/mfbt/tests/moz.build index 0ac17a6faf85..cd3791ff23d6 100644 --- a/mfbt/tests/moz.build +++ b/mfbt/tests/moz.build @@ -15,6 +15,7 @@ CPP_UNIT_TESTS += [ 'TestEnumSet.cpp', 'TestFloatingPoint.cpp', 'TestIntegerPrintfMacros.cpp', + 'TestRollingMean.cpp', 'TestSHA1.cpp', 'TestTypedEnum.cpp', 'TestTypeTraits.cpp',