// Custom includes
#include <cmath>
+#include <cstdlib>
#include <sstream>
#include <senf/Utils/Console/Console.hh>
#include <senf/Utils/Format.hh>
///////////////////////////////////////////////////////////////////////////
// senf::StatisticsBase
-prefix_ void senf::StatisticsBase::enter(float min, float avg, float max, float dev)
+prefix_ void senf::StatisticsBase::enter(unsigned n, float min, float avg, float max, float dev)
{
min_ = min;
avg_ = avg;
max_ = max;
dev_ = dev;
- generateOutput();
- signalChildren();
+ for (unsigned i (0); i < n; ++i)
+ generateOutput();
+ Children::iterator i (children_.begin());
+ Children::iterator const i_end (children_.end());
+ for (; i != i_end; ++i)
+ i->second.enter(n, min_, avg_, max_, dev_);
}
prefix_ senf::Collector & senf::StatisticsBase::operator[](unsigned rank)
}
}
-prefix_ void senf::StatisticsBase::signalChildren()
-{
- Children::iterator i (children_.begin());
- Children::iterator const i_end (children_.end());
- for (; i != i_end; ++i)
- i->second.enter(min_, avg_, max_, dev_);
-}
-
///////////////////////////////////////////////////////////////////////////
// senf::Statistics
///////////////////////////////////////////////////////////////////////////
// senf::Collector
-prefix_ void senf::Collector::enter(float min, float avg, float max, float dev)
+prefix_ void senf::Collector::enter(unsigned n, float min, float avg, float max, float dev)
{
- accSum_ += avg;
- accSumSq_ += avg*avg + dev*dev;
if (min < accMin_) accMin_ = min;
if (max > accMax_) accMax_ = max;
- if (++i_ >= rank_) {
- float accAvg (accSum_ / i_);
- float accDev (std::sqrt(std::max(0.0f,accSumSq_ / i_ - accAvg*accAvg)));
- StatisticsBase::enter(accMin_, accAvg, accMax_, accDev);
- i_ = 0;
+
+ if (i_ + n >= rank_) {
+ accSum_ += (rank_-i_)*avg;
+ accSumSq_ += (rank_-i_)*(rank_-i_)*(avg*avg + dev*dev);
+ float accAvg (accSum_ / rank_);
+ float accDev (std::sqrt(std::max(0.0f,accSumSq_ / rank_ - accAvg*accAvg)));
+ StatisticsBase::enter(1, accMin_, accAvg, accMax_, accDev);
accMin_ = FLT_MAX;
accSum_ = 0.0f;
accSumSq_ = 0.0f;
accMax_ = -FLT_MAX;
+ n -= (rank_ - i_);
+ i_ = 0;
+
+ if (n >= rank_) {
+ std::div_t d (std::div(int(n), int(rank_)));
+ StatisticsBase::enter(d.quot, min, avg, max, dev);
+ n = d.rem;
+ }
+ }
+
+ if (n>0) {
+ accSum_ += n*avg;
+ accSumSq_ += n*n*(avg*avg+dev*dev);
+ i_ += n;
+ if (min < accMin_) accMin_ = min;
+ if (max > accMax_) accMax_ = max;
}
}
///////////////////////////////////////////////////////////////////////////
// senf::Statistics
+prefix_ void senf::Statistics::operator()(unsigned n, float min, float avg, float max,
+ float dev)
+{
+ enter(n, min, avg, max, dev);
+}
+
prefix_ void senf::Statistics::operator()(float min, float avg, float max, float dev)
{
- enter(min, avg, max, dev);
+ enter(1, min, avg, max, dev);
}
prefix_ void senf::Statistics::operator()(float value, float dev)
{
- enter(value, value, value, dev);
+ enter(1, value, value, value, dev);
}
prefix_ senf::StatisticsBase::OutputProxy<senf::Statistics>
protected:
StatisticsBase();
virtual ~StatisticsBase();
- void enter(float min, float avg, float max, float dev);
+ void enter(unsigned n, float min, float avg, float max, float dev);
private:
virtual Statistics & v_base() = 0;
virtual std::string v_path() const = 0;
void generateOutput();
- void signalChildren();
float min_;
float avg_;
Statistics();
- void operator()(float min, float avg, float max, float dev=0.0f);
+ void operator()(unsigned n, float min, float avg, float max, float dev);
///< Enter new data
- /**< This member must be called whenever a new data value is
- available. It is important to call this member \e
- periodically. The frequency at which this member is
- called defines the basic statistics time scale.
+ /**< This member must be called whenever new data is
+ available.
If \a min and \a max values are not available, this
member should be called with \a min, \a avg and \a max
- set to the same value.
-
+ set to the same value. If no error estimate is
+ available, call with \a dev = 0.
+
+ In the most common case, this member is to be called
+ periodically and \a n will be 1 on all calls. The
+ interval, at which this member is called then defines
+ the statistics time scale.
+
+ Calling with \a n > 1 will submit the value \a n
+ times. This makes it possible to aggregate multiple
+ time slices into a single call. This does not change
+ the basic time scale but can change the number of
+ submits per unit time. If the basic time slice is
+ small, this allows to submit values almost arbitrarily
+ non-periodic.
+
+ \param[in] n number of time-slices
\param[in] min minimal data value since last call
\param[in] avg average data value since last call
\param[in] max maximal data values since last call
\param[in] dev standard deviation of avg value */
-
+ void operator()(float min, float avg, float max, float dev=0.0f);
+ ///< Same as operator() with \a n==1
+ /**< Provided so a Statistics instance can be directly used
+ as a signal target. */
void operator()(float value, float dev=0.0f);
- ///< Same as enter() with \a min == \a avg == \a max
+ ///< Same as operator() with \a min == \a avg == \a max
/**< Provided so a Statistics instance can be directly used
as a signal target. */
private:
Collector(StatisticsBase * owner, unsigned rank);
- void enter(float min, float avg, float max, float dev);
+ void enter(unsigned n, float min, float avg, float max, float dev);
Statistics & v_base();
std::string v_path() const;