43cc39f5e5119b66f30ca201e3dc6e640eef3a4e
[senf.git] / senf / Utils / Statistics.cc
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief Statistics non-inline non-template implementation */
30
31 #include "Statistics.hh"
32 //#include "Statistics.ih"
33
34 // Custom includes
35 #include <cmath>
36 #include <cstdlib>
37 #include <sstream>
38 #include <senf/Utils/Format.hh>
39 #include <senf/Utils/Console/STLSupport.hh>
40 #include "StatisticsTargets.hh"
41
42 //#include "Statistics.mpp"
43 #define prefix_
44 //-/////////////////////////////////////////////////////////////////////////////////////////////////
45
46 //-/////////////////////////////////////////////////////////////////////////////////////////////////
47 // senf::StatisticsBase
48
49 prefix_ void senf::StatisticsBase::enter(unsigned n, float min, float avg, float max, float dev)
50 {
51     min_ = min;
52     avg_ = avg;
53     max_ = max;
54     dev_ = dev;
55     for (unsigned i (0); i < n; ++i)
56         generateOutput();
57     Children::iterator i (children_.begin());
58     Children::iterator const i_end  (children_.end());
59     for (; i != i_end; ++i)
60         i->second.enter(n, min_, avg_, max_, dev_);
61 }
62
63 prefix_ senf::Collector & senf::StatisticsBase::operator[](unsigned rank)
64 {
65     Children::iterator i (children_.find(rank));
66     if (i == children_.end())
67         throw InvalidRankException();
68     return i->second;
69 }
70
71 prefix_ senf::Collector const & senf::StatisticsBase::operator[](unsigned rank)
72     const
73 {
74     Children::const_iterator i (children_.find(rank));
75     if (i == children_.end())
76         throw InvalidRankException();
77     return i->second;
78 }
79
80 prefix_ senf::Collector & senf::StatisticsBase::collect(unsigned rank)
81 {
82     std::pair<Children::iterator, bool> state (
83         children_.insert(std::make_pair(rank, Collector(this, rank))) );
84     if (! state.second)
85         throw DuplicateRankException();
86     return state.first->second;
87 }
88
89 prefix_ senf::StatisticsBase::OutputProxy<senf::StatisticsBase>
90 senf::StatisticsBase::output(unsigned n)
91 {
92     OutputMap::iterator i (outputs_.find(n));
93     if (i == outputs_.end()) {
94         i = outputs_.insert(std::make_pair(n, OutputEntry(n))).first;
95         std::stringstream nm;
96         nm << "output" << path() << ":" << n;
97         base().dir.node().add(nm.str(), i->second.dir);
98         detail::StatisticsLoggerRegistry::instance().apply(*this, n, i->second.dir);
99     }
100     if (n > maxQueueLen_)
101         maxQueueLen_ = n;
102     return OutputProxy<StatisticsBase>(this, &(i->second));
103 }
104
105 prefix_ void senf::StatisticsBase::consoleList(unsigned level, std::ostream & os)
106     const
107 {
108     namespace fmt = senf::format;
109
110     os << boost::format("%s%-5d%|15t|  %12.5g  %19.5g  %12.5g\n")
111         % std::string(2*level,' ') % rank()
112         % fmt::eng(min()).setw().showbase() % fmt::eng(avg(),dev()).setw().showbase() % fmt::eng(max()).setw().showbase();
113     {
114         OutputMap::const_iterator i (outputs_.begin());
115         OutputMap::const_iterator i_end (outputs_.end());
116         for (; i != i_end; ++i)
117             os << boost::format("            %3d  %12.5g  %19.5g  %12.5g\n")
118                 % i->second.n
119                 % fmt::eng(i->second.min).setw().showbase()
120                 % fmt::eng(i->second.avg, i->second.dev).setw().showbase()
121                 % fmt::eng(i->second.max).setw().showbase();
122     }
123     {
124         Children::const_iterator i (children_.begin());
125         Children::const_iterator const i_end (children_.end());
126         for (; i != i_end; ++i)
127             i->second.consoleList(level+1, os);
128     }
129 }
130
131 prefix_ void senf::StatisticsBase::generateOutput()
132 {
133     queue_.push_front(QueueEntry(min_, avg_, max_, dev_));
134     while (queue_.size() > maxQueueLen_)
135         queue_.pop_back();
136
137     OutputMap::iterator i (outputs_.begin());
138     OutputMap::iterator const i_end (outputs_.end());
139     for (; i != i_end; ++i) {
140         i->second.min = i->second.avg = i->second.max = i->second.dev = 0.0f;
141         Queue::const_iterator j (queue_.begin());
142         Queue::const_iterator const j_end (queue_.end());
143         unsigned n (0);
144         for (; n < i->second.n && j != j_end; ++n, ++j) {
145             i->second.min += j->min;
146             i->second.avg += j->avg;
147             i->second.max += j->max;
148             i->second.dev += j->dev;
149         }
150         i->second.min /= n;
151         i->second.avg /= n;
152         i->second.max /= n;
153         i->second.dev /= n;
154         i->second.signal(i->second.min, i->second.avg, i->second.max, i->second.dev);
155     }
156 }
157
158 //-/////////////////////////////////////////////////////////////////////////////////////////////////
159 // senf::StatisticsBase::OutputEntry
160
161 prefix_ void senf::StatisticsBase::OutputEntry::consoleList(std::ostream & os)
162 {
163     for (boost::ptr_vector<TargetBase>::iterator i (targets_.begin());
164          i != targets_.end(); ++i)
165         if (! i->label.empty())
166             os << i->label << "\n";
167 }
168
169 //-/////////////////////////////////////////////////////////////////////////////////////////////////
170 // senf::Statistics
171
172 prefix_ senf::Statistics::Statistics()
173 #ifndef SENF_DISABLE_CONSOLE
174     : dir (this)
175 #endif
176 {
177 #ifndef SENF_DISABLE_CONSOLE
178     namespace fty = console::factory;
179
180     dir.add("list", fty::Command(&Statistics::consoleList, this)
181             .doc("List statistics collection intervals and current values.\n"
182                  "\n"
183                  "Columns:\n"
184                  "    RANK    Number of values collected. Since the statistics collectors form\n"
185                  "            a tree, the value is indented according to it's tree location.\n"
186                  "    WIN     Size of output average window.\n"
187                  "    MIN     Last entered minimum value.\n"
188                  "    AVG     Last entered average value.\n"
189                  "    DEV     Standard deviation of average value over the collector rank.\n"
190                  "    MAX     Last entered maximum value.") );
191     dir.add("collect", fty::Command(&Statistics::consoleCollect, this)
192             .doc("Add statistics collection groups. The argument gives a sequence of collector\n"
193                  "ranks each building on the preceding collector:\n"
194                  "\n"
195                  "    $ collect (10 60 60)\n"
196                  "\n"
197                  "Will start by collecting every 10 values together to a new value. 60 of such\n"
198                  "combined values will be collected together in the next step again followed by\n"
199                  "a collection of 60 values. If the statistics is entered with a frequency of\n"
200                  "10 values per second, this will provide combined statistics over the second,\n"
201                  "minutes and hours ranges.\n"
202                  "\n"
203                  "You may call collect multiple times. Any missing collection ranks will be\n"
204                  "added.")
205             .arg("ranks","chain of collector ranks") );
206     dir.add("output", fty::Command(&Statistics::consoleOutput, this)
207             .doc("Generate statistics output. This statement will add an additional output\n"
208                  "generator. This generator will be attached to the collector specified by\n"
209                  "the {rank} parameter. This parameter is a chain of successive rank values\n"
210                  "which specifies the exact collector to use. If the collector does not\n"
211                  "exist, it will be created (this is like automatically calling 'collect'\n"
212                  "with {rank} as argument).\n"
213                  "\n"
214                  "If the output is to be sent somewhere it must be connected to a statistics\n"
215                  "target.\n"
216                  "\n"
217                  "The output may optionally be built using a sliding average over the last\n"
218                  "{window} values.\n"
219                  "\n"
220                  "    $ output ()\n"
221                  "\n"
222                  "will output the basic statistics value each time a new value is entered.\n"
223                  "\n"
224                  "    $ output (10 60) 5\n"
225                  "\n"
226                  "Assuming that new data values are entered 10 times per second, this command\n"
227                  "will generate output once every minute. The value will be the average over\n"
228                  "the last 5 minutes.")
229             .arg("rank","Rank chain selecting the value to generate output for")
230             .arg("window","Optional size of sliding average window",
231                  console::kw::default_value = 1u) );
232 #endif
233 }
234
235 prefix_ void senf::Statistics::consoleList(std::ostream & os)
236 {
237     os << "RANK        WIN       MIN          AVG                   MAX\n";
238     StatisticsBase::consoleList(0, os);
239 }
240
241 prefix_ void senf::Statistics::consoleCollect(std::vector<unsigned> & ranks)
242 {
243     StatisticsBase * stats (this);
244     std::vector<unsigned>::const_iterator i (ranks.begin());
245     std::vector<unsigned>::const_iterator const i_end (ranks.end());
246
247     try {
248         for (; i != i_end; ++i)
249             stats = &(*stats)[*i];
250     }
251     catch (InvalidRankException &) {}
252
253     for (; i != i_end; ++i)
254         stats = & (stats->collect(*i));
255
256 }
257
258 prefix_  boost::shared_ptr<senf::console::DirectoryNode>
259 senf::Statistics::consoleOutput(std::vector<unsigned> & ranks, unsigned window)
260 {
261     StatisticsBase * stats (this);
262     std::vector<unsigned>::const_iterator i (ranks.begin());
263     std::vector<unsigned>::const_iterator const i_end (ranks.end());
264
265     try {
266         for (; i != i_end; ++i)
267             stats = &(*stats)[*i];
268     }
269     catch (InvalidRankException &) {}
270
271     for (; i != i_end; ++i)
272         stats = & (stats->collect(*i));
273
274     return stats->output(window).dir().node().thisptr();
275 }
276
277 prefix_ senf::Statistics & senf::Statistics::v_base()
278 {
279     return *this;
280 }
281
282 prefix_ std::string senf::Statistics::v_path()
283     const
284 {
285     return "";
286 }
287
288 //-/////////////////////////////////////////////////////////////////////////////////////////////////
289 // senf::Collector
290
291 prefix_ void senf::Collector::enter(unsigned n, float min, float avg, float max, float dev)
292 {
293     if (min < accMin_) accMin_ = min;
294     if (max > accMax_) accMax_ = max;
295
296     if (i_ + n >= rank_) {
297         accSum_ += (rank_-i_)*avg;
298         accSumSq_ += (rank_-i_)*(rank_-i_)*(avg*avg + dev*dev);
299         float accAvg (accSum_ / rank_);
300         float accDev (std::sqrt(std::max(0.0f,accSumSq_ / rank_ - accAvg*accAvg)));
301         StatisticsBase::enter(1, accMin_, accAvg, accMax_, accDev);
302         accMin_ = FLT_MAX;
303         accSum_ = 0.0f;
304         accSumSq_ = 0.0f;
305         accMax_ = -FLT_MAX;
306         n -= (rank_ - i_);
307         i_ = 0;
308
309         if (n >= rank_) {
310             std::div_t d (std::div(int(n), int(rank_)));
311             StatisticsBase::enter(d.quot, min, avg, max, dev);
312             n = d.rem;
313         }
314     }
315
316     if (n>0) {
317         accSum_ += n*avg;
318         accSumSq_ += n*n*(avg*avg+dev*dev);
319         i_ += n;
320         if (min < accMin_) accMin_ = min;
321         if (max > accMax_) accMax_ = max;
322     }
323 }
324
325 prefix_ senf::Statistics & senf::Collector::v_base()
326 {
327     return owner_->base();
328 }
329
330 prefix_ std::string senf::Collector::v_path()
331     const
332 {
333     return owner_->path() + "-" + senf::str(rank_);
334 }
335
336 //-/////////////////////////////////////////////////////////////////////////////////////////////////
337 #undef prefix_
338 //#include "Statistics.mpp"
339
340 \f
341 // Local Variables:
342 // mode: c++
343 // fill-column: 100
344 // comment-column: 40
345 // c-file-style: "senf"
346 // indent-tabs-mode: nil
347 // ispell-local-dictionary: "american"
348 // compile-command: "scons -u test"
349 // End: