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