Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / common / perf_histogram.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph - scalable distributed file system
5  *
6  * Copyright (C) 2017 OVH
7  *
8  * This is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License version 2.1, as published by the Free Software
11  * Foundation.  See file COPYING.
12  *
13  */
14
15 #ifndef CEPH_COMMON_PERF_HISTOGRAM_H
16 #define CEPH_COMMON_PERF_HISTOGRAM_H
17
18 #include <array>
19 #include <atomic>
20 #include <memory>
21
22 #include "common/Formatter.h"
23 #include "include/int_types.h"
24 #include "include/assert.h"
25
26 class PerfHistogramCommon {
27 public:
28   enum scale_type_d : uint8_t {
29     SCALE_LINEAR = 1,
30     SCALE_LOG2 = 2,
31   };
32
33   struct axis_config_d {
34     const char *m_name = nullptr;
35     scale_type_d m_scale_type = SCALE_LINEAR;
36     int64_t m_min = 0;
37     int64_t m_quant_size = 0;
38     int32_t m_buckets = 0;
39     axis_config_d() = default;
40     axis_config_d(const char* name,
41                   scale_type_d scale_type,
42                   int64_t min,
43                   int64_t quant_size,
44                   int32_t buckets)
45       : m_name(name),
46         m_scale_type(scale_type),
47         m_min(min),
48         m_quant_size(quant_size),
49         m_buckets(buckets)
50     {}
51   };
52
53 protected:
54   /// Dump configuration of one axis to a formatter
55   static void dump_formatted_axis(ceph::Formatter *f, const axis_config_d &ac);
56
57   /// Quantize given value and convert to bucket number on given axis
58   static int64_t get_bucket_for_axis(int64_t value, const axis_config_d &ac);
59
60   /// Calculate inclusive ranges of axis values for each bucket on that axis
61   static std::vector<std::pair<int64_t, int64_t>> get_axis_bucket_ranges(
62       const axis_config_d &ac);
63 };
64
65 /// PerfHistogram does trace a histogram of input values. It's an extended
66 /// version of a standard histogram which does trace characteristics of a single
67 /// one value only. In this implementation, values can be traced in multiple
68 /// dimensions - i.e. we can create a histogram of input request size (first
69 /// dimension) and processing latency (second dimension). Creating standard
70 /// histogram out of such multidimensional one is trivial and requires summing
71 /// values across dimensions we're not interested in.
72 template <int DIM = 2>
73 class PerfHistogram : public PerfHistogramCommon {
74 public:
75   /// Initialize new histogram object
76   PerfHistogram(std::initializer_list<axis_config_d> axes_config) {
77     assert(axes_config.size() == DIM &&
78            "Invalid number of axis configuration objects");
79
80     int i = 0;
81     for (const auto &ac : axes_config) {
82       assert(ac.m_buckets > 0 && "Must have at least one bucket on axis");
83       assert(ac.m_quant_size > 0 &&
84              "Quantization unit must be non-zero positive integer value");
85
86       m_axes_config[i++] = ac;
87     }
88
89     m_rawData.reset(new std::atomic<uint64_t>[get_raw_size()] {});
90   }
91
92   /// Copy from other histogram object
93   PerfHistogram(const PerfHistogram &other)
94       : m_axes_config(other.m_axes_config) {
95     int64_t size = get_raw_size();
96     m_rawData.reset(new std::atomic<uint64_t>[size] {});
97     for (int64_t i = 0; i < size; i++) {
98       m_rawData[i] = other.m_rawData[i].load();
99     }
100   }
101
102   /// Set all histogram values to 0
103   void reset() {
104     auto size = get_raw_size();
105     for (auto i = size; --i >= 0;) {
106       m_rawData[i] = 0;
107     }
108   }
109
110   /// Increase counter for given axis values by one
111   template <typename... T>
112   void inc(T... axis) {
113     auto index = get_raw_index_for_value(axis...);
114     m_rawData[index]++;
115   }
116
117   /// Increase counter for given axis buckets by one
118   template <typename... T>
119   void inc_bucket(T... bucket) {
120     auto index = get_raw_index_for_bucket(bucket...);
121     m_rawData[index]++;
122   }
123
124   /// Read value from given bucket
125   template <typename... T>
126   uint64_t read_bucket(T... bucket) const {
127     auto index = get_raw_index_for_bucket(bucket...);
128     return m_rawData[index];
129   }
130
131   /// Dump data to a Formatter object
132   void dump_formatted(ceph::Formatter *f) const {
133     // Dump axes configuration
134     f->open_array_section("axes");
135     for (auto &ac : m_axes_config) {
136       dump_formatted_axis(f, ac);
137     }
138     f->close_section();
139
140     // Dump histogram values
141     dump_formatted_values(f);
142   }
143
144 protected:
145   /// Raw data stored as linear space, internal indexes are calculated on
146   /// demand.
147   std::unique_ptr<std::atomic<uint64_t>[]> m_rawData;
148
149   /// Configuration of axes
150   std::array<axis_config_d, DIM> m_axes_config;
151
152   /// Dump histogram counters to a formatter
153   void dump_formatted_values(ceph::Formatter *f) const {
154     visit_values([f](int) { f->open_array_section("values"); },
155                  [f](int64_t value) { f->dump_unsigned("value", value); },
156                  [f](int) { f->close_section(); });
157   }
158
159   /// Get number of all histogram counters
160   int64_t get_raw_size() {
161     int64_t ret = 1;
162     for (const auto &ac : m_axes_config) {
163       ret *= ac.m_buckets;
164     }
165     return ret;
166   }
167
168   /// Calculate m_rawData index from axis values
169   template <typename... T>
170   int64_t get_raw_index_for_value(T... axes) const {
171     static_assert(sizeof...(T) == DIM, "Incorrect number of arguments");
172     return get_raw_index_internal<0>(get_bucket_for_axis, 0, axes...);
173   }
174
175   /// Calculate m_rawData index from axis bucket numbers
176   template <typename... T>
177   int64_t get_raw_index_for_bucket(T... buckets) const {
178     static_assert(sizeof...(T) == DIM, "Incorrect number of arguments");
179     return get_raw_index_internal<0>(
180         [](int64_t bucket, const axis_config_d &ac) {
181           assert(bucket >= 0 && "Bucket index can not be negative");
182           assert(bucket < ac.m_buckets && "Bucket index too large");
183           return bucket;
184         },
185         0, buckets...);
186   }
187
188   template <int level = 0, typename F, typename... T>
189   int64_t get_raw_index_internal(F bucket_evaluator, int64_t startIndex,
190                                  int64_t value, T... tail) const {
191     static_assert(level + 1 + sizeof...(T) == DIM,
192                   "Internal consistency check");
193     auto &ac = m_axes_config[level];
194     auto bucket = bucket_evaluator(value, ac);
195     return get_raw_index_internal<level + 1>(
196         bucket_evaluator, ac.m_buckets * startIndex + bucket, tail...);
197   }
198
199   template <int level, typename F>
200   int64_t get_raw_index_internal(F, int64_t startIndex) const {
201     static_assert(level == DIM, "Internal consistency check");
202     return startIndex;
203   }
204
205   /// Visit all histogram counters, call onDimensionEnter / onDimensionLeave
206   /// when starting / finishing traversal
207   /// on given axis, call onValue when dumping raw histogram counter value.
208   template <typename FDE, typename FV, typename FDL>
209   void visit_values(FDE onDimensionEnter, FV onValue, FDL onDimensionLeave,
210                     int level = 0, int startIndex = 0) const {
211     if (level == DIM) {
212       onValue(m_rawData[startIndex]);
213       return;
214     }
215
216     onDimensionEnter(level);
217     auto &ac = m_axes_config[level];
218     startIndex *= ac.m_buckets;
219     for (int32_t i = 0; i < ac.m_buckets; ++i, ++startIndex) {
220       visit_values(onDimensionEnter, onValue, onDimensionLeave, level + 1,
221                    startIndex);
222     }
223     onDimensionLeave(level);
224   }
225 };
226
227 #endif