Only show user-presets in favorite sidebar
[ardour.git] / libs / pbd / pbd / timing.h
1 /*
2     Copyright (C) 2014 Tim Mayberry
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifndef __libpbd_timing_h__
21 #define __libpbd_timing_h__
22
23 #include <glib.h>
24
25 #include <stdint.h>
26
27 #include <cmath>
28 #include <limits>
29 #include <string>
30 #include <vector>
31
32 #include "pbd/libpbd_visibility.h"
33
34 #ifdef COMPILER_MSVC
35 #undef min
36 #undef max
37 #endif
38
39 namespace PBD {
40
41 LIBPBD_API bool get_min_max_avg_total (const std::vector<uint64_t>& values, uint64_t& min, uint64_t& max, uint64_t& avg, uint64_t& total);
42
43 LIBPBD_API std::string timing_summary (const std::vector<uint64_t>& values);
44
45 /**
46  * This class allows collecting timing data using two different
47  * techniques. The first is using start() and update() and then
48  * calling elapsed() to get the elapsed time. This is useful when
49  * you want to measure the elapsed time between two different
50  * execution points. e.g
51  *
52  * timing.start();
53  * do_stuff();
54  * timing.update();
55  * cerr << "do_stuff took: "
56  *      << timing.elapsed()
57  *      << "usecs" << endl;
58  *
59  * The other is timing intervals using start() and calling
60  * get_interval() periodically to measure the time intervals
61  * between the same execution point. The difference is necessary
62  * to get the most accurate timing information when timing
63  * intervals but I didn't feel it necessary to have two separate
64  * classes.
65  */
66 class LIBPBD_API Timing
67 {
68 public:
69
70         Timing ()
71                 : m_start_val(0)
72                 , m_last_val(0)
73         { start ();}
74
75         bool valid () const {
76                 return (m_start_val != 0 && m_last_val != 0);
77         }
78
79         void start () {
80                 m_start_val = g_get_monotonic_time ();
81                 m_last_val = 0;
82         }
83
84         void update () {
85                 m_last_val = g_get_monotonic_time ();
86         }
87
88         void reset () {
89                 m_start_val = m_last_val = 0;
90         }
91
92         uint64_t get_interval () {
93                 uint64_t elapsed = 0;
94                 update ();
95                 if (valid()) {
96                         elapsed = m_last_val - m_start_val;
97                         m_start_val = m_last_val;
98                         m_last_val = 0;
99                 }
100                 return elapsed;
101         }
102
103         /// @return Elapsed time in microseconds
104         uint64_t elapsed () const {
105                 return m_last_val - m_start_val;
106         }
107
108         /// @return Elapsed time in milliseconds
109         uint64_t elapsed_msecs () const {
110                 return elapsed () / 1000;
111         }
112
113 private:
114
115         uint64_t m_start_val;
116         uint64_t m_last_val;
117
118 };
119
120 class LIBPBD_API TimingStats : public Timing
121 {
122 public:
123         TimingStats ()
124         {
125                 /* override implicit Timing::start () */
126                 reset ();
127         }
128
129         void update ()
130         {
131                 Timing::update ();
132                 calc ();
133         }
134
135         void reset ()
136         {
137                 Timing::reset ();
138                 _min = std::numeric_limits<uint64_t>::max();
139                 _max = 0;
140                 _cnt = 0;
141                 _avg = 0.;
142                 _vm  = 0.;
143                 _vs  = 0.;
144         }
145
146         bool valid () const {
147                 return Timing::valid () && _cnt > 1;
148         }
149
150         bool get_stats (uint64_t& min,
151                         uint64_t& max,
152                         double& avg,
153                         double& dev) const
154         {
155                 if (_cnt < 2) {
156                         return false;
157                 }
158                 min = _min;
159                 max = _max;
160                 avg = _avg / (double)_cnt;
161                 dev = sqrt (_vs / (_cnt - 1.0));
162                 return true;
163         }
164
165 private:
166         void calc ()
167         {
168                 const uint64_t diff = elapsed ();
169
170                 _avg += diff;
171
172                 if (diff > _max) {
173                         _max = diff;
174                 }
175                 if (diff < _min) {
176                         _min = diff;
177                 }
178
179                 if (_cnt == 0) {
180                         _vm = diff;
181                 } else {
182                         const double ela = diff;
183                         const double var_m1 = _vm;
184                         _vm = _vm + (ela - _vm) / (1.0 + _cnt);
185                         _vs = _vs + (ela - _vm) * (ela - var_m1);
186                 }
187                 ++_cnt;
188         }
189
190         uint64_t _cnt;
191         uint64_t _min;
192         uint64_t _max;
193         double   _avg;
194         double   _vm;
195         double   _vs;
196 };
197
198 class LIBPBD_API TimingData
199 {
200 public:
201         TimingData () : m_reserve_size(256)
202         { reset (); }
203
204         void start_timing () {
205                 m_timing.start ();
206         }
207
208         void add_elapsed () {
209                 m_timing.update ();
210                 if (m_timing.valid()) {
211                         m_elapsed_values.push_back (m_timing.elapsed());
212                 }
213         }
214
215         void add_interval () {
216                 uint64_t interval = m_timing.get_interval ();
217                 m_elapsed_values.push_back (interval);
218         }
219
220         void reset () {
221                 m_elapsed_values.clear ();
222                 m_elapsed_values.reserve (m_reserve_size);
223         }
224
225         std::string summary () const
226         { return timing_summary (m_elapsed_values); }
227
228         bool get_min_max_avg_total (uint64_t& min,
229                                     uint64_t& max,
230                                     uint64_t& avg,
231                                     uint64_t& total) const
232         { return PBD::get_min_max_avg_total (m_elapsed_values, min, max, avg, total); }
233
234         void reserve (uint32_t reserve_size)
235         { m_reserve_size = reserve_size; reset (); }
236
237         uint32_t size () const
238         { return m_elapsed_values.size(); }
239
240 private:
241
242         Timing m_timing;
243
244         uint32_t m_reserve_size;
245
246         std::vector<uint64_t> m_elapsed_values;
247 };
248
249 class LIBPBD_API Timed
250 {
251 public:
252         Timed (TimingData& data)
253                 : m_data(data)
254         {
255                 m_data.start_timing ();
256         }
257
258         ~Timed ()
259         {
260                 m_data.add_elapsed ();
261         }
262
263 private:
264
265         TimingData& m_data;
266
267 };
268
269 } // namespace PBD
270
271 #endif // __libpbd_timing_h__