site_scons: support for clang/llvm
[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     const
237 {
238     os << "RANK        WIN       MIN          AVG                   MAX\n";
239     StatisticsBase::consoleList(0, os);
240 }
241
242 prefix_ void senf::Statistics::consoleCollect(std::vector<unsigned> & ranks)
243 {
244     StatisticsBase * stats (this);
245     std::vector<unsigned>::const_iterator i (ranks.begin());
246     std::vector<unsigned>::const_iterator const i_end (ranks.end());
247
248     try {
249         for (; i != i_end; ++i)
250             stats = &(*stats)[*i];
251     }
252     catch (InvalidRankException &) {}
253
254     for (; i != i_end; ++i)
255         stats = & (stats->collect(*i));
256
257 }
258
259 prefix_  boost::shared_ptr<senf::console::DirectoryNode>
260 senf::Statistics::consoleOutput(std::vector<unsigned> & ranks, unsigned window)
261 {
262     StatisticsBase * stats (this);
263     std::vector<unsigned>::const_iterator i (ranks.begin());
264     std::vector<unsigned>::const_iterator const i_end (ranks.end());
265
266     try {
267         for (; i != i_end; ++i)
268             stats = &(*stats)[*i];
269     }
270     catch (InvalidRankException &) {}
271
272     for (; i != i_end; ++i)
273         stats = & (stats->collect(*i));
274
275     return stats->output(window).dir().node().thisptr();
276 }
277
278 prefix_ senf::Statistics & senf::Statistics::v_base()
279 {
280     return *this;
281 }
282
283 prefix_ std::string senf::Statistics::v_path()
284     const
285 {
286     return "";
287 }
288
289 //-/////////////////////////////////////////////////////////////////////////////////////////////////
290 // senf::Collector
291
292 prefix_ void senf::Collector::enter(unsigned n, float min, float avg, float max, float dev)
293 {
294     if (min < accMin_) accMin_ = min;
295     if (max > accMax_) accMax_ = max;
296
297     if (i_ + n >= rank_) {
298         accSum_ += (rank_-i_)*avg;
299         accSumSq_ += (rank_-i_)*(rank_-i_)*(avg*avg + dev*dev);
300         float accAvg (accSum_ / rank_);
301         float accDev (std::sqrt(std::max(0.0f,accSumSq_ / rank_ - accAvg*accAvg)));
302         StatisticsBase::enter(1, accMin_, accAvg, accMax_, accDev);
303         accMin_ = FLT_MAX;
304         accSum_ = 0.0f;
305         accSumSq_ = 0.0f;
306         accMax_ = -FLT_MAX;
307         n -= (rank_ - i_);
308         i_ = 0;
309
310         if (n >= rank_) {
311             std::div_t d (std::div(int(n), int(rank_)));
312             StatisticsBase::enter(d.quot, min, avg, max, dev);
313             n = d.rem;
314         }
315     }
316
317     if (n>0) {
318         accSum_ += n*avg;
319         accSumSq_ += n*n*(avg*avg+dev*dev);
320         i_ += n;
321         if (min < accMin_) accMin_ = min;
322         if (max > accMax_) accMax_ = max;
323     }
324 }
325
326 prefix_ senf::Statistics & senf::Collector::v_base()
327 {
328     return owner_->base();
329 }
330
331 prefix_ std::string senf::Collector::v_path()
332     const
333 {
334     return owner_->path() + "-" + senf::str(rank_);
335 }
336
337 //-/////////////////////////////////////////////////////////////////////////////////////////////////
338 #undef prefix_
339 //#include "Statistics.mpp"
340
341 \f
342 // Local Variables:
343 // mode: c++
344 // fill-column: 100
345 // comment-column: 40
346 // c-file-style: "senf"
347 // indent-tabs-mode: nil
348 // ispell-local-dictionary: "american"
349 // compile-command: "scons -u test"
350 // End: