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
22 #include "common/Formatter.h"
23 #include "include/int_types.h"
24 #include "include/ceph_assert.h"
26 class PerfHistogramCommon
{
28 enum scale_type_d
: uint8_t {
33 struct axis_config_d
{
34 const char *m_name
= nullptr;
35 scale_type_d m_scale_type
= SCALE_LINEAR
;
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
,
46 m_scale_type(scale_type
),
48 m_quant_size(quant_size
),
54 /// Dump configuration of one axis to a formatter
55 static void dump_formatted_axis(ceph::Formatter
*f
, const axis_config_d
&ac
);
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
);
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
);
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
{
75 /// Initialize new histogram object
76 PerfHistogram(std::initializer_list
<axis_config_d
> axes_config
) {
77 ceph_assert(axes_config
.size() == DIM
&&
78 "Invalid number of axis configuration objects");
81 for (const auto &ac
: axes_config
) {
82 ceph_assertf(ac
.m_buckets
> 0, "Must have at least one bucket on axis");
83 ceph_assertf(ac
.m_quant_size
> 0,
84 "Quantization unit must be non-zero positive integer value");
86 m_axes_config
[i
++] = ac
;
89 m_rawData
.reset(new std::atomic
<uint64_t>[get_raw_size()] {});
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();
102 /// Set all histogram values to 0
104 auto size
= get_raw_size();
105 for (auto i
= size
; --i
>= 0;) {
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
...);
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
...);
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
];
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
);
140 // Dump histogram values
141 dump_formatted_values(f
);
145 /// Raw data stored as linear space, internal indexes are calculated on
147 std::unique_ptr
<std::atomic
<uint64_t>[]> m_rawData
;
149 /// Configuration of axes
150 std::array
<axis_config_d
, DIM
> m_axes_config
;
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(); });
159 /// Get number of all histogram counters
160 int64_t get_raw_size() {
162 for (const auto &ac
: m_axes_config
) {
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
...);
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 ceph_assertf(bucket
>= 0, "Bucket index can not be negative");
182 ceph_assertf(bucket
< ac
.m_buckets
, "Bucket index too large");
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
...);
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");
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 {
212 onValue(m_rawData
[startIndex
]);
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,
223 onDimensionLeave(level
);