Although having very little C++ experience, I was tasked to implement several statistics objects that are safe to be asynchronous accessed by different threads, that collect data in relatively small memory caches and do the math when asked. Many of those I need to write are based on averaging, so help me by inspecting basic averaging class. I just hope to avoid any strategic errors in design of my cargo-cult code-gluing efforts.
#include <mutex>
template <typename T, int N> class Average {
T data[N];
int head;
double avg;
int itr;
int counter;
std::mutex _mutex;
public:
Average() : head(0), avg(0.), itr(0), counter(0) {
};
void empty() {
std::unique_lock<std::mutex> lock(this->_mutex);
head = 0;
avg = 0.;
itr = 0;
counter = 0;
}
int count() {
std::unique_lock<std::mutex> lock(this->_mutex);
return counter;
}
void push(T value) {
std::unique_lock<std::mutex> lock(this->_mutex);
counter++;
data [head] = value;
double tmp = 0.;
if (++head == N) {
head = 0;
for (int i = 0; i < N; i++) {
tmp = tmp + data[i];
}
tmp = tmp / N;
avg = (avg * itr) + tmp;
itr += 1;
avg = avg / itr;
}
}
double average() {
std::unique_lock<std::mutex> lock(this->_mutex);
double tmp = 0.;
if (head == 0){
if (itr == 0) return 0.0;
return avg;
}
for (int i = 0; i < head; i++) {
tmp = tmp + data[i];
}
if (itr == 0) return tmp / head;
tmp = tmp/N;
return ((avg * itr) + tmp) / ((double)itr + (double)head/(double)N);
}
};
As an usage example, by extending the Average class, I implemented a RunningAverage class which averages only let say last N pushed values. I got a worker pool that perform Monte Carlo simulation where it is preferred to maintain step acceptance rate 0.5+/-0.1 and when rate falls outside, I adjust model parameters. So I got something like:
#define ACCEPT_STEP 1
#define DECLINE_STEP 0
RunningAverage <float, ACC_BUFFER_LEN> global_acceptance_rate;
and thread do something like:
global_acceptance_rate.push(ACCEPT_STEP); // or
global_acceptance_rate.push(DECLINE_STEP);
So this allows me to dynamically control simulation.
count()
? It doesn’t modify anything. Is it dangerous to read a variable while it is being updated elsewhere? I’m just curious... \$\endgroup\$int32_t
have been updated, bytes 3 and 4 haven't yet), which could cause problems. Thestd::unique_lock
would prevent that from happening. (It isn't an issue forint
on x86 platforms, but that cannot be generalized to all platforms.) \$\endgroup\$