CUBRID Engine  latest
monitor_statistic.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 //
20 // monitor_statistic.hpp - base interface for monitoring statistics
21 //
22 // in this header can be found the most basic statistics definitions
23 //
24 // cubmonitor statistic concept is defined by two functions: fetch and collect.
25 //
26 // collect (change) can vary on type of statistics and is called by active transactions and other engine threads.
27 // as a rule, it should be fast and non-intrusive.
28 //
29 // fetch function returns the statistic's value. it is also the function used by monitor to fetch registered
30 // statistics.
31 //
32 // Based on type of collections, there are currently four types of statistics:
33 //
34 // 1. accumulator - values are added up in stored value.
35 // 2. gauge - updates stored value.
36 // 3. max - stores maximum value of all given values.
37 // 4. min - stores minimum value of all given values.
38 //
39 // Based on statistic's collected data representation, there are three types:
40 //
41 // 1. amount (e.g. counters and other types of units).
42 // 2. floating (e.g. ratio, percentage).
43 // 3. time (all timers).
44 //
45 // For all statistic types, an atomic way of synchronizing is also defined.
46 //
47 // Templates are used to define each type of collector. Fully specialized statistics will each have one fetch and one
48 // collect function. Fully specialized statistic are named:
49 //
50 // valuetype_collectype[_atomic]_statistic.
51 //
52 // So a time accumulator that is shared by many transactions and requires atomic access is called:
53 //
54 // time_accumulator_atomic_statistic.
55 //
56 // How to use:
57 //
58 // // use a statistic to time execution
59 // cubmonitor::time_accumulator_atomic_statistic my_timer_stat;
60 //
61 // cubmonitor::time_point start_pt = cubmonitor::clock_type::now ();
62 //
63 // // do some operations
64 //
65 // cubmonitor::time_point end_pt = cubmonitor::clock_type::now ();
66 // my_timer_stat.collect (end_pt - start_pt);
67 // // fetch and print time value
68 // std::cout << "execution time is " << my_timer_stat.fetch () << " microseconds." << std::endl;
69 //
70 
71 #if !defined _MONITOR_STATISTIC_HPP_
72 #define _MONITOR_STATISTIC_HPP_
73 
74 #include "monitor_definition.hpp"
75 
76 #include <atomic>
77 #include <chrono>
78 #include <limits>
79 
80 #include <cinttypes>
81 
82 namespace cubmonitor
83 {
85  // Statistic collected data memory representation
87 
88  // aliases for usual memory representation of statistics (and the atomic counterparts):
89  // amount type
90  using amount_rep = std::uint64_t;
91  // floating type
92  using floating_rep = double;
93  // time type
94  using time_rep = duration;
95 
98 
101 
104 
106  // Primitive classes for basic statistics.
107  //
108  // Primitive interface includes fetch-able concept required for monitoring registration:
109  //
110  // void fetch (statistic_value *, fetch_mode) const; // fetch statistics (global, transaction sheets)
111  // // primitives do not actually keep transaction sheets
112  // std::size_t get_statistics_count (void) const; // statistics count; for primitive is always 1
113  //
114  // For statistics and collector structures:
115  //
116  // Rep get_value (fetch_mode); // return current value
117  // void set_value (const Rep& value); // replace current value
118  //
119  // Atomic primitives will also include additional function:
120  // void compare_exchange (Rep& compare_value, const Rep& replace_value); // atomic compare & exchange
121  //
122  // Rep template represents the type of data primitive can interact with (get/set type). This can be amount_rep,
123  // floating_rep or time_rep.
124  //
126 
127  template <typename Rep>
128  class primitive
129  {
130  public:
131  using rep = Rep; // alias for data representation
132 
133  primitive (Rep value = Rep ()) // constructor
134  : m_value (value)
135  {
136  //
137  }
138 
140  {
141  m_value = other.m_value;
142  return *this;
143  }
144 
145  // fetch interface for monitor registration - statistics count (always 1 for primitives) and fetch value function
146  inline void fetch (statistic_value *destination, fetch_mode mode = FETCH_GLOBAL) const;
147  std::size_t get_statistics_count (void) const
148  {
149  return 1;
150  }
151 
152  // get current value
153  // NOTE: this function is very expensive for some reason
155  {
156  return m_value;
157  }
158 
159  protected:
160  // set new value
161  void set_value (const Rep &value)
162  {
163  m_value = value;
164  }
165 
166  Rep m_value; // stored value
167  };
168 
169  template <typename Rep>
171  {
172  public:
173  using rep = Rep; // alias for data representation
174 
175  atomic_primitive (Rep value = Rep ()) // constructor
176  : m_value (value)
177  {
178  //
179  }
180 
182  {
183  m_value.store (other.m_value.load ());
184  return *this;
185  }
186 
187  // fetch interface for monitor registration - statistics count (always 1 for primitives) and fetch value function
188  inline void fetch (statistic_value *destination, fetch_mode mode = FETCH_GLOBAL) const;
189  std::size_t get_statistics_count (void) const
190  {
191  return 1;
192  }
193 
194  // get current value
196  {
197  return m_value;
198  }
199 
200  protected:
201  // set new value
202  void set_value (const Rep &value)
203  {
204  m_value = value;
205  }
206 
207  void fetch_add (const Rep &value);
208 
209  // atomic compare & exchange
210  bool compare_exchange (Rep &compare_value, const Rep &replace_value)
211  {
212  return m_value.compare_exchange_strong (compare_value, replace_value);
213  }
214 
215  std::atomic<Rep> m_value; // stored value
216  };
217 
218  // different specialization for time_rep because there is no such thing as atomic duration;
219  // we have to store std::atomic<time_rep::rep>
220  template <>
222  {
223  public:
224  using rep = time_rep; // alias for data representation
225 
226  atomic_primitive (time_rep value = time_rep ()) // constructor
227  : m_value (value.count ())
228  {
229  //
230  }
231 
233  {
234  m_value.store (other.m_value.load ());
235  return *this;
236  }
237 
238  // fetch interface for monitor registration - statistics count (always 1 for primitives) and fetch value function
239  inline void fetch (statistic_value *destination, fetch_mode mode = FETCH_GLOBAL) const;
240  std::size_t get_statistics_count (void) const
241  {
242  return 1;
243  }
244 
245  // get current value
247  {
248  if (mode == FETCH_GLOBAL)
249  {
250  return time_rep (m_value.load ());
251  }
252  else
253  {
254  return time_rep ();
255  }
256  }
257 
258  protected:
259  // set new value
260  void set_value (const time_rep &value)
261  {
262  m_value = value.count ();
263  }
264 
265  void fetch_add (const time_rep &value)
266  {
267  (void) m_value.fetch_add (value.count ());
268  }
269 
270  // atomic compare & exchange
271  bool compare_exchange (time_rep &compare_value, const time_rep &replace_value)
272  {
273  time_rep::rep compare_count = compare_value.count ();
274  return m_value.compare_exchange_strong (compare_count, replace_value.count ());
275  }
276 
277  std::atomic<time_rep::rep> m_value; // stored value
278  };
279 
280  // specialize
281  template class primitive<amount_rep>;
282  template class atomic_primitive<amount_rep>;
283  template class primitive<floating_rep>;
284 #if defined (MONITOR_ENABLE_ATOMIC_FLOATING_REP)
285  template class atomic_primitive<floating_rep>;
286 #endif // MONITOR_ENABLE_ATOMIC_FLOATING_REP
287  template class primitive<time_rep>;
288  // template class atomic_primitive<time_rep>; // differentely specialized, see above
289 
291  // Accumulator statistics
293  // accumulator statistic - add change to existing value
294  template<class Rep>
296  {
297  public:
298  using rep = Rep; // collected data representation
299 
300  void collect (const Rep &value); // collect value
301  };
302  // accumulator atomic statistic - atomic add change to existing value
303  template<class Rep>
305  {
306  public:
307  using rep = Rep; // collected data representation
308 
309  void collect (const Rep &value); // collect value
310  };
311 
313  // Gauge statistics
315  // gauge statistic - replace current value with change
316  template<class Rep>
318  {
319  public:
320  using rep = Rep; // collected data representation
321 
322  void collect (const Rep &value); // collect value
323  };
324  // gauge atomic statistic - test and set current value
325  template<class Rep>
327  {
328  public:
329  using rep = Rep; // collected data representation
330 
331  void collect (const Rep &value); // collect value
332  };
333 
335  // Max statistics
337  // max statistic - compare with current value and set if change is bigger
338  template<class Rep>
340  {
341  public:
342  using rep = Rep; // collected data representation
343 
344  max_statistic (void); // constructor
345  void collect (const Rep &value); // collect value
346  };
347  // max atomic statistic - compare and exchange with current value if change is bigger
348  template<class Rep>
350  {
351  public:
352  using rep = Rep; // collected data representation
353 
354  max_atomic_statistic (void); // constructor
355  void collect (const Rep &value); // collect value
356  };
357 
359  // Min statistics
361 
362  // min statistic - compare with current value and set if change is smaller
363  template<class Rep>
365  {
366  public:
367  using rep = Rep; // collected data representation
368 
369  min_statistic (void); // constructor
370  void collect (const Rep &value);
371  };
372  // min atomic statistic - compare and exchange with current value if change is smaller
373  template<class Rep>
375  {
376  public:
377  using rep = Rep; // collected data representation
378 
379  min_atomic_statistic (void); // constructor
380  void collect (const Rep &value); // collect value
381  };
382 
384  // specializations
386 
387  // no synchronization specializations
391 
395 
399 
403 
404  // atomic synchronization specializations
406 #if defined (MONITOR_ENABLE_ATOMIC_FLOATING_REP)
407  using floating_accumulator_atomic_statistic = accumulator_atomic_statistic<floating_rep>;
408 #endif // MONITOR_ENABLE_ATOMIC_FLOATING_REP
410 
412 #if defined (MONITOR_ENABLE_ATOMIC_FLOATING_REP)
413  using floating_gauge_atomic_statistic = gauge_atomic_statistic<floating_rep>;
414 #endif // MONITOR_ENABLE_ATOMIC_FLOATING_REP
416 
418 #if defined (MONITOR_ENABLE_ATOMIC_FLOATING_REP)
419  using floating_max_atomic_statistic = max_atomic_statistic<floating_rep>;
420 #endif // MONITOR_ENABLE_ATOMIC_FLOATING_REP
422 
424 #if defined (MONITOR_ENABLE_ATOMIC_FLOATING_REP)
425  using floating_min_atomic_statistic = min_atomic_statistic<floating_rep>;
426 #endif // MONITOR_ENABLE_ATOMIC_FLOATING_REP
428 
430  // template/inline implementation
432 
434  // class primitive
436 
437  template <typename Rep>
438  void
439  primitive<Rep>::fetch (statistic_value *destination, fetch_mode mode /* = FETCH_GLOBAL */) const
440  {
441  if (mode == FETCH_TRANSACTION_SHEET)
442  {
443  // no transaction sheet
444  return;
445  }
446  *destination = statistic_value_cast (m_value);
447  }
448 
449  template <typename Rep>
450  void
451  atomic_primitive<Rep>::fetch (statistic_value *destination, fetch_mode mode /* = FETCH_GLOBAL */) const
452  {
453  if (mode == FETCH_TRANSACTION_SHEET)
454  {
455  // no transaction sheet
456  return;
457  }
458  *destination = statistic_value_cast (get_value ());
459  }
460 
461 #if defined (MONITOR_ENABLE_ATOMIC_FLOATING_REP)
462  template <>
463  void
465  {
466  // fetch_add does not work for floating_rep
467  floating_rep crt_value;
468  do
469  {
470  crt_value = m_value;
471  }
472  while (!compare_exchange (crt_value, crt_value + value));
473  }
474 #endif // MONITOR_ENABLE_ATOMIC_FLOATING_REP
475 
476  template <typename Rep>
477  void
479  {
480  m_value.fetch_add (value);
481  }
482 
483  void
484  atomic_primitive<time_rep>::fetch (statistic_value *destination, fetch_mode mode /* = FETCH_GLOBAL */) const
485  {
486  if (mode == FETCH_TRANSACTION_SHEET)
487  {
488  // no transaction sheet
489  return;
490  }
491  *destination = statistic_value_cast (get_value ());
492  }
493 
494  template <typename Rep>
495  void
497  {
498  this->m_value += value;
499  }
500 
501  template <>
502  void
504 
505  template <typename Rep>
506  void
508  {
509  this->m_value.fetch_add (value);
510  }
511 
512  template <typename Rep>
513  void
514  gauge_statistic<Rep>::collect (const Rep &value)
515  {
516  this->set_value (value);
517  }
518 
519  template <typename Rep>
520  void
522  {
523  this->set_value (value);
524  }
525 
526  template <typename Rep>
527  void
528  max_statistic<Rep>::collect (const Rep &value)
529  {
530  if (value > this->get_value ())
531  {
532  this->set_value (value);
533  }
534  }
535 
536  template <typename Rep>
537  void
539  {
540  Rep loaded;
541 
542  // loop until either:
543  // 1. current value is better
544  // 2. successfully replaced value
545 
546  do
547  {
548  loaded = this->get_value ();
549  if (loaded >= value)
550  {
551  // not bigger
552  return;
553  }
554  // exchange
555  }
556  while (!this->compare_exchange (loaded, value));
557  }
558 
559  template <typename Rep>
560  void
561  min_statistic<Rep>::collect (const Rep &value)
562  {
563  if (value < this->get_value ())
564  {
565  this->set_value (value);
566  }
567  }
568 
569  template <typename Rep>
570  void
572  {
573  Rep loaded;
574 
575  // loop until either:
576  // 1. current value is better
577  // 2. successfully replaced value
578 
579  do
580  {
581  loaded = this->get_value ();
582  if (loaded <= value)
583  {
584  // not smaller
585  return;
586  }
587  // try exchange
588  }
589  while (!compare_exchange (loaded, value));
590  // exchange successful
591  }
592 
593 } // namespace cubmonitor
594 
595 #endif // _MONITOR_STATISTIC_HPP_
void set_value(const Rep &value)
bool compare_exchange(Rep &compare_value, const Rep &replace_value)
std::size_t get_statistics_count(void) const
const fetch_mode FETCH_GLOBAL
Rep get_value(fetch_mode mode=FETCH_GLOBAL) const
std::uint64_t statistic_value
void fetch(statistic_value *destination, fetch_mode mode=FETCH_GLOBAL) const
clock_type::duration duration
bool compare_exchange(time_rep &compare_value, const time_rep &replace_value)
primitive & operator=(const primitive &other)
void fetch_add(const Rep &value)
static enum scanner_mode mode
void collect(const Rep &value)
floating_rep floating_rep_cast(statistic_value value)
void fetch(statistic_value *destination, fetch_mode mode=FETCH_GLOBAL) const
int count(int &result, const cub_regex_object &reg, const std::string &src, const int position, const INTL_CODESET codeset)
std::size_t get_statistics_count(void) const
void collect(const Rep &value)
Rep get_value(fetch_mode mode=FETCH_GLOBAL) const
amount_rep amount_rep_cast(statistic_value value)
primitive(Rep value=Rep())
time_rep get_value(fetch_mode mode=FETCH_GLOBAL) const
void collect(const Rep &value)
statistic_value statistic_value_cast(const amount_rep &rep)
time_rep time_rep_cast(statistic_value value)
std::uint64_t amount_rep
const fetch_mode FETCH_TRANSACTION_SHEET
void set_value(const Rep &value)