1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2017 OVH
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.
15 #ifndef CEPH_COMMON_PERF_HISTOGRAM_H
16 #define CEPH_COMMON_PERF_HISTOGRAM_H
18 #include "common/Formatter.h"
19 #include "include/int_types.h"
25 #include "include/ceph_assert.h"
27 class PerfHistogramCommon
{
29 enum scale_type_d
: uint8_t {
34 struct axis_config_d
{
35 const char *m_name
= nullptr;
36 scale_type_d m_scale_type
= SCALE_LINEAR
;
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
,
47 m_scale_type(scale_type
),
49 m_quant_size(quant_size
),
55 /// Dump configuration of one axis to a formatter
56 static void dump_formatted_axis(ceph::Formatter
*f
, const axis_config_d
&ac
);
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
);
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
);
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
{
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");
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");
88 m_axes_config
[i
++] = ac
;
91 m_rawData
.reset(new std::atomic
<uint64_t>[get_raw_size()]);
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
];
104 /// Set all histogram values to 0
106 auto size
= get_raw_size();
107 for (auto i
= size
; --i
>= 0;) {
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;
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;
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
];
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
);
142 // Dump histogram values
143 dump_formatted_values(f
);
147 /// Raw data stored as linear space, internal indexes are calculated on
149 std::unique_ptr
<std::atomic
<uint64_t>[]> m_rawData
;
151 /// Configuration of axes
152 std::array
<axis_config_d
, DIM
> m_axes_config
;
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(); });
161 /// Get number of all histogram counters
162 int64_t get_raw_size() {
164 for (const auto &ac
: m_axes_config
) {
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
...);
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");
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
...);
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");
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 {
214 onValue(m_rawData
[startIndex
]);
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,
225 onDimensionLeave(level
);