mirror of
https://github.com/lubosz/overte.git
synced 2025-04-12 07:18:21 +02:00
added RingBufferHistory template class, used it in SentPacketHistory and MovingMinMaxAvg
This commit is contained in:
parent
a8cf199256
commit
81e168f657
5 changed files with 234 additions and 135 deletions
|
@ -14,8 +14,6 @@
|
|||
|
||||
SentPacketHistory::SentPacketHistory(int size)
|
||||
: _sentPackets(size),
|
||||
_newestPacketAt(0),
|
||||
_numExistingPackets(0),
|
||||
_newestSequenceNumber(std::numeric_limits<uint16_t>::max())
|
||||
{
|
||||
}
|
||||
|
@ -29,16 +27,8 @@ void SentPacketHistory::packetSent(uint16_t sequenceNumber, const QByteArray& pa
|
|||
qDebug() << "Unexpected sequence number passed to SentPacketHistory::packetSent()!"
|
||||
<< "Expected:" << expectedSequenceNumber << "Actual:" << sequenceNumber;
|
||||
}
|
||||
|
||||
_newestSequenceNumber = sequenceNumber;
|
||||
|
||||
// increment _newestPacketAt cyclically, insert new packet there.
|
||||
// this will overwrite the oldest packet in the buffer
|
||||
_newestPacketAt = (_newestPacketAt == _sentPackets.size() - 1) ? 0 : _newestPacketAt + 1;
|
||||
_sentPackets[_newestPacketAt] = packet;
|
||||
if (_numExistingPackets < _sentPackets.size()) {
|
||||
_numExistingPackets++;
|
||||
}
|
||||
_sentPackets.insert(packet);
|
||||
}
|
||||
|
||||
const QByteArray* SentPacketHistory::getPacket(uint16_t sequenceNumber) const {
|
||||
|
@ -51,13 +41,6 @@ const QByteArray* SentPacketHistory::getPacket(uint16_t sequenceNumber) const {
|
|||
if (seqDiff < 0) {
|
||||
seqDiff += UINT16_RANGE;
|
||||
}
|
||||
// if desired sequence number is too old to be found in the history, return null
|
||||
if (seqDiff >= _numExistingPackets) {
|
||||
return NULL;
|
||||
}
|
||||
int packetAt = _newestPacketAt - seqDiff;
|
||||
if (packetAt < 0) {
|
||||
packetAt += _sentPackets.size();
|
||||
}
|
||||
return &_sentPackets.at(packetAt);
|
||||
|
||||
return _sentPackets.get(seqDiff);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <qbytearray.h>
|
||||
#include <qvector.h>
|
||||
#include "RingBufferHistory.h"
|
||||
|
||||
#include "SequenceNumberStats.h"
|
||||
|
||||
|
@ -26,9 +26,7 @@ public:
|
|||
const QByteArray* getPacket(uint16_t sequenceNumber) const;
|
||||
|
||||
private:
|
||||
QVector<QByteArray> _sentPackets; // circular buffer
|
||||
int _newestPacketAt;
|
||||
int _numExistingPackets;
|
||||
RingBufferHistory<QByteArray> _sentPackets; // circular buffer
|
||||
|
||||
uint16_t _newestSequenceNumber;
|
||||
};
|
||||
|
|
|
@ -12,9 +12,48 @@
|
|||
#ifndef hifi_MovingMinMaxAvg_h
|
||||
#define hifi_MovingMinMaxAvg_h
|
||||
|
||||
#include "RingBufferHistory.h"
|
||||
|
||||
template <typename T>
|
||||
class MovingMinMaxAvg {
|
||||
|
||||
private:
|
||||
class Stats {
|
||||
public:
|
||||
Stats()
|
||||
: _min(std::numeric_limits<T>::max()),
|
||||
_max(std::numeric_limits<T>::min()),
|
||||
_average(0.0) {}
|
||||
|
||||
void updateWithSample(T sample, int& numSamplesInAverage) {
|
||||
if (sample < _min) {
|
||||
_min = sample;
|
||||
}
|
||||
if (sample > _max) {
|
||||
_max = sample;
|
||||
}
|
||||
_average = _average * ((double)numSamplesInAverage / (numSamplesInAverage + 1))
|
||||
+ (double)sample / (numSamplesInAverage + 1);
|
||||
numSamplesInAverage++;
|
||||
}
|
||||
|
||||
void updateWithOtherStats(const Stats& other, int& numStatsInAverage) {
|
||||
if (other._min < _min) {
|
||||
_min = other._min;
|
||||
}
|
||||
if (other._max > _max) {
|
||||
_max = other._max;
|
||||
}
|
||||
_average = _average * ((double)numStatsInAverage / (numStatsInAverage + 1))
|
||||
+ other._average / (numStatsInAverage + 1);
|
||||
numStatsInAverage++;
|
||||
}
|
||||
|
||||
T _min;
|
||||
T _max;
|
||||
double _average;
|
||||
};
|
||||
|
||||
public:
|
||||
// This class collects 3 stats (min, max, avg) over a moving window of samples.
|
||||
// The moving window contains _windowIntervals * _intervalLength samples.
|
||||
|
@ -25,104 +64,51 @@ public:
|
|||
// new sample, instantiate this class with MovingMinMaxAvg(1, 100).
|
||||
|
||||
MovingMinMaxAvg(int intervalLength, int windowIntervals)
|
||||
: _min(std::numeric_limits<T>::max()),
|
||||
_max(std::numeric_limits<T>::min()),
|
||||
_average(0.0),
|
||||
_samplesCollected(0),
|
||||
_intervalLength(intervalLength),
|
||||
: _intervalLength(intervalLength),
|
||||
_windowIntervals(windowIntervals),
|
||||
_overallStats(),
|
||||
_samplesCollected(0),
|
||||
_windowStats(),
|
||||
_existingSamplesInCurrentInterval(0),
|
||||
_existingIntervals(0),
|
||||
_windowMin(std::numeric_limits<T>::max()),
|
||||
_windowMax(std::numeric_limits<T>::min()),
|
||||
_windowAverage(0.0),
|
||||
_currentIntervalMin(std::numeric_limits<T>::max()),
|
||||
_currentIntervalMax(std::numeric_limits<T>::min()),
|
||||
_currentIntervalAverage(0.0),
|
||||
_newestIntervalStatsAt(0),
|
||||
_currentIntervalStats(),
|
||||
_intervalStats(windowIntervals),
|
||||
_newStatsAvailable(false)
|
||||
{
|
||||
_intervalMins = new T[_windowIntervals];
|
||||
_intervalMaxes = new T[_windowIntervals];
|
||||
_intervalAverages = new double[_windowIntervals];
|
||||
}
|
||||
|
||||
~MovingMinMaxAvg() {
|
||||
delete[] _intervalMins;
|
||||
delete[] _intervalMaxes;
|
||||
delete[] _intervalAverages;
|
||||
}
|
||||
{}
|
||||
|
||||
void reset() {
|
||||
_min = std::numeric_limits<T>::max();
|
||||
_max = std::numeric_limits<T>::min();
|
||||
_average = 0.0;
|
||||
_overallStats = Stats();
|
||||
_samplesCollected = 0;
|
||||
_windowStats = Stats();
|
||||
_existingSamplesInCurrentInterval = 0;
|
||||
_existingIntervals = 0;
|
||||
_windowMin = std::numeric_limits<T>::max();
|
||||
_windowMax = std::numeric_limits<T>::min();
|
||||
_windowAverage = 0.0;
|
||||
_currentIntervalMin = std::numeric_limits<T>::max();
|
||||
_currentIntervalMax = std::numeric_limits<T>::min();
|
||||
_currentIntervalAverage = 0.0;
|
||||
_newStatsAvailableFlag = false;
|
||||
_currentIntervalStats = Stats();
|
||||
_intervalStats.clear();
|
||||
_newStatsAvailable = false;
|
||||
}
|
||||
|
||||
void update(T newSample) {
|
||||
|
||||
// update overall stats
|
||||
if (newSample < _min) {
|
||||
_min = newSample;
|
||||
}
|
||||
if (newSample > _max) {
|
||||
_max = newSample;
|
||||
}
|
||||
updateAverage(_average, _samplesCollected, (double)newSample);
|
||||
_overallStats.updateWithSample(newSample, _samplesCollected);
|
||||
|
||||
// update the current interval stats
|
||||
if (newSample < _currentIntervalMin) {
|
||||
_currentIntervalMin = newSample;
|
||||
}
|
||||
if (newSample > _currentIntervalMax) {
|
||||
_currentIntervalMax = newSample;
|
||||
}
|
||||
updateAverage(_currentIntervalAverage, _existingSamplesInCurrentInterval, (double)newSample);
|
||||
_currentIntervalStats.updateWithSample(newSample, _existingSamplesInCurrentInterval);
|
||||
|
||||
// if the current interval of samples is now full, record its stats into our past intervals' stats
|
||||
if (_existingSamplesInCurrentInterval == _intervalLength) {
|
||||
|
||||
// increment index of the newest interval's stats cyclically
|
||||
_newestIntervalStatsAt = _newestIntervalStatsAt == _windowIntervals - 1 ? 0 : _newestIntervalStatsAt + 1;
|
||||
|
||||
// record current interval's stats, then reset them
|
||||
_intervalMins[_newestIntervalStatsAt] = _currentIntervalMin;
|
||||
_intervalMaxes[_newestIntervalStatsAt] = _currentIntervalMax;
|
||||
_intervalAverages[_newestIntervalStatsAt] = _currentIntervalAverage;
|
||||
_currentIntervalMin = std::numeric_limits<T>::max();
|
||||
_currentIntervalMax = std::numeric_limits<T>::min();
|
||||
_currentIntervalAverage = 0.0;
|
||||
_intervalStats.insert(_currentIntervalStats);
|
||||
_currentIntervalStats = Stats();
|
||||
_existingSamplesInCurrentInterval = 0;
|
||||
|
||||
if (_existingIntervals < _windowIntervals) {
|
||||
_existingIntervals++;
|
||||
}
|
||||
|
||||
// update the window's stats
|
||||
int k = _newestIntervalStatsAt;
|
||||
_windowMin = _intervalMins[k];
|
||||
_windowMax = _intervalMaxes[k];
|
||||
_windowAverage = _intervalAverages[k];
|
||||
int intervalsIncludedInWindowStats = 1;
|
||||
while (intervalsIncludedInWindowStats < _existingIntervals) {
|
||||
k = k == 0 ? _windowIntervals - 1 : k - 1;
|
||||
if (_intervalMins[k] < _windowMin) {
|
||||
_windowMin = _intervalMins[k];
|
||||
}
|
||||
if (_intervalMaxes[k] > _windowMax) {
|
||||
_windowMax = _intervalMaxes[k];
|
||||
}
|
||||
updateAverage(_windowAverage, intervalsIncludedInWindowStats, _intervalAverages[k]);
|
||||
// update the window's stats by combining the intervals' stats
|
||||
RingBufferHistory<Stats>::Iterator i = _intervalStats.begin();
|
||||
RingBufferHistory<Stats>::Iterator end = _intervalStats.end();
|
||||
_windowStats = Stats();
|
||||
int intervalsIncludedInWindowStats = 0;
|
||||
while (i != end) {
|
||||
_windowStats.updateWithOtherStats(*i, intervalsIncludedInWindowStats);
|
||||
i++;
|
||||
}
|
||||
|
||||
_newStatsAvailable = true;
|
||||
|
@ -133,48 +119,34 @@ public:
|
|||
bool getNewStatsAvailableFlag() const { return _newStatsAvailable; }
|
||||
void clearNewStatsAvailableFlag() { _newStatsAvailable = false; }
|
||||
|
||||
T getMin() const { return _min; }
|
||||
T getMax() const { return _max; }
|
||||
double getAverage() const { return _average; }
|
||||
T getWindowMin() const { return _windowMin; }
|
||||
T getWindowMax() const { return _windowMax; }
|
||||
double getWindowAverage() const { return _windowAverage; }
|
||||
T getMin() const { return _overallStats._min; }
|
||||
T getMax() const { return _overallStats._max; }
|
||||
double getAverage() const { return _overallStats._average; }
|
||||
T getWindowMin() const { return _windowStats._min; }
|
||||
T getWindowMax() const { return _windowStats._max; }
|
||||
double getWindowAverage() const { return _windowStats._average; }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
void updateAverage(double& average, int& numSamples, double newSample) {
|
||||
// update some running average without overflowing it
|
||||
average = average * ((double)numSamples / (numSamples + 1)) + newSample / (numSamples + 1);
|
||||
numSamples++;
|
||||
}
|
||||
|
||||
private:
|
||||
// these are min/max/avg stats for all samples collected.
|
||||
T _min;
|
||||
T _max;
|
||||
double _average;
|
||||
int _samplesCollected;
|
||||
|
||||
int _intervalLength;
|
||||
int _windowIntervals;
|
||||
|
||||
int _existingSamplesInCurrentInterval;
|
||||
int _existingIntervals;
|
||||
// these are min/max/avg stats for all samples collected.
|
||||
Stats _overallStats;
|
||||
int _samplesCollected;
|
||||
|
||||
// these are the min/max/avg stats for the samples in the moving window
|
||||
T _windowMin;
|
||||
T _windowMax;
|
||||
double _windowAverage;
|
||||
Stats _windowStats;
|
||||
int _existingSamplesInCurrentInterval;
|
||||
|
||||
T _currentIntervalMin;
|
||||
T _currentIntervalMax;
|
||||
double _currentIntervalAverage;
|
||||
// these are the min/max/avg stats for the current interval
|
||||
Stats _currentIntervalStats;
|
||||
|
||||
T* _intervalMins;
|
||||
T* _intervalMaxes;
|
||||
double* _intervalAverages;
|
||||
int _newestIntervalStatsAt;
|
||||
// these are stored stats for the past intervals in the window
|
||||
RingBufferHistory<Stats> _intervalStats;
|
||||
|
||||
bool _newStatsAvailable;
|
||||
};
|
||||
|
||||
#endif // hifi_OctalCode_h
|
||||
#endif // hifi_MovingMinMaxAvg_h
|
||||
|
|
146
libraries/shared/src/RingBufferHistory.h
Normal file
146
libraries/shared/src/RingBufferHistory.h
Normal file
|
@ -0,0 +1,146 @@
|
|||
//
|
||||
// RingBufferHistory.h
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Yixin Wang on 7/9/2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_RingBufferHistory_h
|
||||
#define hifi_RingBufferHistory_h
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <iterator>
|
||||
|
||||
template <typename T>
|
||||
class RingBufferHistory {
|
||||
|
||||
public:
|
||||
|
||||
RingBufferHistory(int capacity)
|
||||
: _size(capacity + 1),
|
||||
_capacity(capacity),
|
||||
_newestEntryAt(0),
|
||||
_numEntries(0)
|
||||
{
|
||||
_buffer = new T[_size];
|
||||
}
|
||||
|
||||
RingBufferHistory(const RingBufferHistory& other)
|
||||
: _size(other._size),
|
||||
_capacity(other._capacity),
|
||||
_newestEntryAt(other._newestEntryAt),
|
||||
_numEntries(other._numEntries)
|
||||
{
|
||||
_buffer = new T[_size];
|
||||
memcpy(_buffer, other._buffer, _size*sizeof(T));
|
||||
}
|
||||
|
||||
RingBufferHistory& operator= (const RingBufferHistory& rhs) {
|
||||
_size = rhs._size;
|
||||
_capacity = rhs._capacity;
|
||||
_newestEntryAt = rhs._newestEntryAt;
|
||||
_numEntries = rhs._numEntries;
|
||||
delete[] _buffer;
|
||||
_buffer = new T[_size];
|
||||
memcpy(_buffer, rhs._buffer, _size*sizeof(T));
|
||||
}
|
||||
|
||||
~RingBufferHistory() {
|
||||
delete[] _buffer;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_numEntries = 0;
|
||||
}
|
||||
|
||||
void insert(const T& entry) {
|
||||
// increment newest entry index cyclically
|
||||
_newestEntryAt = (_newestEntryAt == _size - 1) ? 0 : _newestEntryAt + 1;
|
||||
|
||||
// insert new entry
|
||||
_buffer[_newestEntryAt] = entry;
|
||||
if (_numEntries < _capacity) {
|
||||
_numEntries++;
|
||||
}
|
||||
}
|
||||
|
||||
// 0 retrieves the most recent entry, _numEntries - 1 retrieves the oldest.
|
||||
// returns NULL if entryAge not within [0, _numEntries-1]
|
||||
const T* get(int entryAge) const {
|
||||
if (!(entryAge >= 0 && entryAge < _numEntries)) {
|
||||
return NULL;
|
||||
}
|
||||
int entryAt = _newestEntryAt - entryAge;
|
||||
if (entryAt < 0) {
|
||||
entryAt += _size;
|
||||
}
|
||||
return &_buffer[entryAt];
|
||||
}
|
||||
|
||||
T* get(int entryAge) {
|
||||
return const_cast<T*>((static_cast<const RingBufferHistory*>(this))->get(entryAge));
|
||||
}
|
||||
|
||||
const T* getNewestEntry() const {
|
||||
return &_buffer[_newestEntryAt];
|
||||
}
|
||||
|
||||
T* getNewestEntry() {
|
||||
return &_buffer[_newestEntryAt];
|
||||
}
|
||||
|
||||
int getCapacity() const { return _capacity; }
|
||||
int getNumEntries() const { return _numEntries; }
|
||||
|
||||
private:
|
||||
T* _buffer;
|
||||
int _size;
|
||||
int _capacity;
|
||||
int _newestEntryAt;
|
||||
int _numEntries;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
class Iterator : public std::iterator < std::forward_iterator_tag, T > {
|
||||
public:
|
||||
Iterator(T* buffer, int size, T* at) : _buffer(buffer), _bufferEnd(buffer+size), _at(at) {}
|
||||
|
||||
bool operator==(const Iterator& rhs) { return _at == rhs._at; }
|
||||
bool operator!=(const Iterator& rhs) { return _at != rhs._at; }
|
||||
T& operator*() { return *_at; }
|
||||
T* operator->() { return _at; }
|
||||
|
||||
Iterator& operator++() {
|
||||
_at = (_at == _buffer) ? _bufferEnd - 1 : _at - 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator& operator++(int) {
|
||||
Iterator tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
T* const _buffer;
|
||||
T* const _bufferEnd;
|
||||
T* _at;
|
||||
};
|
||||
|
||||
Iterator begin() { return Iterator(_buffer, _size, &_buffer[_newestEntryAt]); }
|
||||
|
||||
Iterator end() {
|
||||
int endAt = _newestEntryAt - _numEntries;
|
||||
if (endAt < 0) {
|
||||
endAt += _size;
|
||||
}
|
||||
return Iterator(_buffer, _size, &_buffer[endAt]);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // hifi_RingBufferHistory_h
|
|
@ -59,7 +59,7 @@ void MovingMinMaxAvgTests::runAllTests() {
|
|||
|
||||
assert(stats.getMin() == min);
|
||||
assert(stats.getMax() == max);
|
||||
assert(abs(stats.getAverage() / average - 1.0) < 0.000001);
|
||||
assert(abs(stats.getAverage() / average - 1.0) < 0.000001 || abs(stats.getAverage() - average) < 0.000001);
|
||||
|
||||
if ((i + 1) % INTERVAL_LENGTH == 0) {
|
||||
|
||||
|
@ -78,7 +78,7 @@ void MovingMinMaxAvgTests::runAllTests() {
|
|||
|
||||
assert(stats.getWindowMin() == windowMin);
|
||||
assert(stats.getWindowMax() == windowMax);
|
||||
assert(abs(stats.getAverage() / average - 1.0) < 0.000001);
|
||||
assert(abs(stats.getAverage() / average - 1.0) < 0.000001 || abs(stats.getAverage() - average) < 0.000001);
|
||||
|
||||
} else {
|
||||
assert(!stats.getNewStatsAvailableFlag());
|
||||
|
|
Loading…
Reference in a new issue