site_scons: support for clang/llvm
[senf.git] / senf / Utils / Statistics.cc
index a2363ca..6513828 100644 (file)
@@ -1,24 +1,29 @@
 // $Id$
 //
-// Copyright (C) 2008 
+// Copyright (C) 2008
 // Fraunhofer Institute for Open Communication Systems (FOKUS)
-// Competence Center NETwork research (NET), St. Augustin, GERMANY
-//     Stefan Bund <g0dil@berlios.de>
 //
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
+// The contents of this file are subject to the Fraunhofer FOKUS Public License
+// Version 1.0 (the "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at 
+// http://senf.berlios.de/license.html
 //
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
+// The Fraunhofer FOKUS Public License Version 1.0 is based on, 
+// but modifies the Mozilla Public License Version 1.1.
+// See the full license text for the amendments.
 //
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the
-// Free Software Foundation, Inc.,
-// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+// Software distributed under the License is distributed on an "AS IS" basis, 
+// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
+// for the specific language governing rights and limitations under the License.
+//
+// The Original Code is Fraunhofer FOKUS code.
+//
+// The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
+// (registered association), Hansastraße 27 c, 80686 Munich, Germany.
+// All Rights Reserved.
+//
+// Contributor(s):
+//   Stefan Bund <g0dil@berlios.de>
 
 /** \file
     \brief Statistics non-inline non-template implementation */
 //#include "Statistics.ih"
 
 // Custom includes
+#include <cmath>
+#include <cstdlib>
 #include <sstream>
-#include <senf/Utils/Console/Console.hh>
+#include <senf/Utils/Format.hh>
+#include <senf/Utils/Console/STLSupport.hh>
 #include "StatisticsTargets.hh"
 
 //#include "Statistics.mpp"
 #define prefix_
-///////////////////////////////cc.p////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
 
-///////////////////////////////////////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
 // senf::StatisticsBase
 
-prefix_ void senf::StatisticsBase::enter(float min, float avg, float max)
+prefix_ void senf::StatisticsBase::enter(unsigned n, float min, float avg, float max, float dev)
 {
     min_ = min;
     avg_ = avg;
     max_ = max;
-    generateOutput();
-    signalChildren();
+    dev_ = dev;
+    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)
@@ -55,6 +68,15 @@ prefix_ senf::Collector & senf::StatisticsBase::operator[](unsigned rank)
     return i->second;
 }
 
+prefix_ senf::Collector const & senf::StatisticsBase::operator[](unsigned rank)
+    const
+{
+    Children::const_iterator i (children_.find(rank));
+    if (i == children_.end())
+        throw InvalidRankException();
+    return i->second;
+}
+
 prefix_ senf::Collector & senf::StatisticsBase::collect(unsigned rank)
 {
     std::pair<Children::iterator, bool> state (
@@ -80,52 +102,23 @@ senf::StatisticsBase::output(unsigned n)
     return OutputProxy<StatisticsBase>(this, &(i->second));
 }
 
-//
-// generate an engineering style notation 
-//
-char *format_eng( float f)
-{
-    static char buf[16];
-    if (f > 0) {
-        int n = 0;
-        while( f >= 1000.0f) {
-                f /= 1000.f;
-                n+=3;
-        }
-
-        sprintf( buf, " %3.2fe%+03d", f, n);
-    }
-    else if (f < 0) {
-        int n = 0;
-        while( f <= -1000.0f) {
-                f *= 1000.f;
-                n+=3;
-        }
-
-        sprintf( buf, "%3.2fe%+03d", f, n);
-    }
-    else{
-        sprintf( buf, "        0.00");
-    }
-
-    return buf;
-}
-
-
 prefix_ void senf::StatisticsBase::consoleList(unsigned level, std::ostream & os)
     const
 {
-    os << boost::format("%s%-5d%|15t|  %12s  %12s  %12s\n") 
-        % std::string(2*level,' ') % rank() % format_eng(min()) % format_eng(avg()) % format_eng(max());
+    namespace fmt = senf::format;
+
+    os << boost::format("%s%-5d%|15t|  %12.5g  %19.5g  %12.5g\n")
+        % std::string(2*level,' ') % rank()
+        % fmt::eng(min()).setw().showbase() % fmt::eng(avg(),dev()).setw().showbase() % fmt::eng(max()).setw().showbase();
     {
         OutputMap::const_iterator i (outputs_.begin());
         OutputMap::const_iterator i_end (outputs_.end());
         for (; i != i_end; ++i)
-            os << boost::format("            %3d  %12s  %12s  %12s\n")
-                % i->second.n 
-                % format_eng(i->second.min/i->second.n) 
-                % format_eng(i->second.avg/i->second.n)
-                % format_eng(i->second.max/i->second.n);
+            os << boost::format("            %3d  %12.5g  %19.5g  %12.5g\n")
+                % i->second.n
+                % fmt::eng(i->second.min).setw().showbase()
+                % fmt::eng(i->second.avg, i->second.dev).setw().showbase()
+                % fmt::eng(i->second.max).setw().showbase();
     }
     {
         Children::const_iterator i (children_.begin());
@@ -137,14 +130,14 @@ prefix_ void senf::StatisticsBase::consoleList(unsigned level, std::ostream & os
 
 prefix_ void senf::StatisticsBase::generateOutput()
 {
-    queue_.push_front(QueueEntry(min_, avg_, max_));
+    queue_.push_front(QueueEntry(min_, avg_, max_, dev_));
     while (queue_.size() > maxQueueLen_)
         queue_.pop_back();
 
     OutputMap::iterator i (outputs_.begin());
     OutputMap::iterator const i_end (outputs_.end());
     for (; i != i_end; ++i) {
-        i->second.min = i->second.avg = i->second.max = 0.0f;
+        i->second.min = i->second.avg = i->second.max = i->second.dev = 0.0f;
         Queue::const_iterator j (queue_.begin());
         Queue::const_iterator const j_end (queue_.end());
         unsigned n (0);
@@ -152,20 +145,28 @@ prefix_ void senf::StatisticsBase::generateOutput()
             i->second.min += j->min;
             i->second.avg += j->avg;
             i->second.max += j->max;
+            i->second.dev += j->dev;
         }
-        i->second.signal(i->second.min/n, i->second.avg/n, i->second.max/n);
+        i->second.min /= n;
+        i->second.avg /= n;
+        i->second.max /= n;
+        i->second.dev /= n;
+        i->second.signal(i->second.min, i->second.avg, i->second.max, i->second.dev);
     }
 }
 
-prefix_ void senf::StatisticsBase::signalChildren()
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
+// senf::StatisticsBase::OutputEntry
+
+prefix_ void senf::StatisticsBase::OutputEntry::consoleList(std::ostream & os)
 {
-    Children::iterator i (children_.begin());
-    Children::iterator const i_end  (children_.end());
-    for (; i != i_end; ++i)
-        i->second.enter(min_, avg_, max_);
+    for (boost::ptr_vector<TargetBase>::iterator i (targets_.begin());
+         i != targets_.end(); ++i)
+        if (! i->label.empty())
+            os << i->label << "\n";
 }
 
-///////////////////////////////////////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
 // senf::Statistics
 
 prefix_ senf::Statistics::Statistics()
@@ -174,63 +175,67 @@ prefix_ senf::Statistics::Statistics()
 #endif
 {
 #ifndef SENF_DISABLE_CONSOLE
-    dir.add("list", &Statistics::consoleList)
-        .doc("List statistics collection intervals and current values.\n"
-             "\n"
-             "Columns:\n"
-             "    RANK    Number of values collected. Since the statistics collectors form\n"
-             "            a tree, the value is indented according to it's tree location.\n"
-             "    WIN     Size of output average window.\n"
-             "    MIN     Last entered minimum value.\n"
-             "    AVG     Last entered average value.\n"
-             "    MAX     Last entered maximum value.");
-    dir.add("collect", &Statistics::consoleCollect)
-        .doc("Add statistics collection groups. The argument gives a sequence of collector\n"
-             "ranks each building on the preceding collector:\n"
-             "\n"
-             "    $ collect (10 60 60)\n"
-             "\n"
-             "Will start by collecting every 10 values together to a new value. 60 of such\n"
-             "combined values will be collected together in the next step again followed by\n"
-             "a collection of 60 values. If the statistics is entered with a frequency of\n"
-             "10 values per second, this will provide combined statistics over the second,\n"
-             "minutes and hours ranges.\n"
-             "\n"
-             "You may call collect multiple times. Any missing collection ranks will be\n"
-             "added.")
-        .arg("ranks","chain of collector ranks");
-    dir.add("output", &Statistics::consoleOutput)
-        .doc("Generate statistics output. This statement will add an additional output\n"
-             "generator. This generator will be attached to the collector specified by\n"
-             "the {rank} parameter. This parameter is a chain of successive rank values\n"
-             "which specifies the exact collector to use. If the collector does not\n"
-             "exist, it will be created (this is like automatically calling 'collect'\n"
-             "with {rank} as argument).\n"
-             "\n"
-             "If the output is to be sent somewhere it must be connected to a statistics\n"
-             "target.\n"
-             "\n"
-             "The output may optionally be built using a sliding average over the last\n"
-             "{window} values.\n"
-             "\n"
-             "    $ output ()\n"
-             "\n"
-             "will output the basic statistics value each time a new value is entered.\n"
-             "\n"
-             "    $ output (10 60) 5\n"
-             "\n"
-             "Assuming that new data values are entered 10 times per second, this command\n"
-             "will generate output once every minute. The value will be the average over\n"
-             "the last 5 minutes.")
-        .arg("rank","Rank chain selecting the value to generate output for")
-        .arg("window","Optional size of sliding average window",
-             senf::console::kw::default_value = 1u);
+    namespace fty = console::factory;
+
+    dir.add("list", fty::Command(&Statistics::consoleList, this)
+            .doc("List statistics collection intervals and current values.\n"
+                 "\n"
+                 "Columns:\n"
+                 "    RANK    Number of values collected. Since the statistics collectors form\n"
+                 "            a tree, the value is indented according to it's tree location.\n"
+                 "    WIN     Size of output average window.\n"
+                 "    MIN     Last entered minimum value.\n"
+                 "    AVG     Last entered average value.\n"
+                 "    DEV     Standard deviation of average value over the collector rank.\n"
+                 "    MAX     Last entered maximum value.") );
+    dir.add("collect", fty::Command(&Statistics::consoleCollect, this)
+            .doc("Add statistics collection groups. The argument gives a sequence of collector\n"
+                 "ranks each building on the preceding collector:\n"
+                 "\n"
+                 "    $ collect (10 60 60)\n"
+                 "\n"
+                 "Will start by collecting every 10 values together to a new value. 60 of such\n"
+                 "combined values will be collected together in the next step again followed by\n"
+                 "a collection of 60 values. If the statistics is entered with a frequency of\n"
+                 "10 values per second, this will provide combined statistics over the second,\n"
+                 "minutes and hours ranges.\n"
+                 "\n"
+                 "You may call collect multiple times. Any missing collection ranks will be\n"
+                 "added.")
+            .arg("ranks","chain of collector ranks") );
+    dir.add("output", fty::Command(&Statistics::consoleOutput, this)
+            .doc("Generate statistics output. This statement will add an additional output\n"
+                 "generator. This generator will be attached to the collector specified by\n"
+                 "the {rank} parameter. This parameter is a chain of successive rank values\n"
+                 "which specifies the exact collector to use. If the collector does not\n"
+                 "exist, it will be created (this is like automatically calling 'collect'\n"
+                 "with {rank} as argument).\n"
+                 "\n"
+                 "If the output is to be sent somewhere it must be connected to a statistics\n"
+                 "target.\n"
+                 "\n"
+                 "The output may optionally be built using a sliding average over the last\n"
+                 "{window} values.\n"
+                 "\n"
+                 "    $ output ()\n"
+                 "\n"
+                 "will output the basic statistics value each time a new value is entered.\n"
+                 "\n"
+                 "    $ output (10 60) 5\n"
+                 "\n"
+                 "Assuming that new data values are entered 10 times per second, this command\n"
+                 "will generate output once every minute. The value will be the average over\n"
+                 "the last 5 minutes.")
+            .arg("rank","Rank chain selecting the value to generate output for")
+            .arg("window","Optional size of sliding average window",
+                 console::kw::default_value = 1u) );
 #endif
 }
 
 prefix_ void senf::Statistics::consoleList(std::ostream & os)
+    const
 {
-    os << "RANK        WIN           MIN           AVG           MAX\n";
+    os << "RANK        WIN       MIN          AVG                   MAX\n";
     StatisticsBase::consoleList(0, os);
 }
 
@@ -248,7 +253,7 @@ prefix_ void senf::Statistics::consoleCollect(std::vector<unsigned> & ranks)
 
     for (; i != i_end; ++i)
         stats = & (stats->collect(*i));
-        
+
 }
 
 prefix_  boost::shared_ptr<senf::console::DirectoryNode>
@@ -257,7 +262,7 @@ senf::Statistics::consoleOutput(std::vector<unsigned> & ranks, unsigned window)
     StatisticsBase * stats (this);
     std::vector<unsigned>::const_iterator i (ranks.begin());
     std::vector<unsigned>::const_iterator const i_end (ranks.end());
-    
+
     try {
         for (; i != i_end; ++i)
             stats = &(*stats)[*i];
@@ -266,7 +271,7 @@ senf::Statistics::consoleOutput(std::vector<unsigned> & ranks, unsigned window)
 
     for (; i != i_end; ++i)
         stats = & (stats->collect(*i));
-    
+
     return stats->output(window).dir().node().thisptr();
 }
 
@@ -281,20 +286,40 @@ prefix_ std::string senf::Statistics::v_path()
     return "";
 }
 
-///////////////////////////////////////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
 // senf::Collector
 
-prefix_ void senf::Collector::enter(float min, float avg, float max)
+prefix_ void senf::Collector::enter(unsigned n, float min, float avg, float max, float dev)
 {
-    accAvg_ += avg;
     if (min < accMin_) accMin_ = min;
     if (max > accMax_) accMax_ = max;
-    if (++i_ >= rank_) {
-        StatisticsBase::enter(accMin_, accAvg_ / i_, accMax_);
-        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;
-        accAvg_ = 0.0f;
+        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;
     }
 }
 
@@ -309,7 +334,7 @@ prefix_ std::string senf::Collector::v_path()
     return owner_->path() + "-" + senf::str(rank_);
 }
 
-///////////////////////////////cc.e////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
 #undef prefix_
 //#include "Statistics.mpp"