mirror of
https://github.com/ceph/ceph
synced 2024-12-25 21:03:31 +00:00
5009d9ecc6
This conflicts with the system assert.h so rename and change includes to reflect the new name. Fixes: http://tracker.ceph.com/issues/35682 Signed-off-by: Brad Hubbard <bhubbard@redhat.com>
230 lines
7.0 KiB
C++
230 lines
7.0 KiB
C++
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
|
// vim: ts=8 sw=2 smarttab
|
|
/*
|
|
* Ceph - scalable distributed file system
|
|
*
|
|
* Copyright (C) 2017 OVH
|
|
*
|
|
* This is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License version 2.1, as published by the Free Software
|
|
* Foundation. See file COPYING.
|
|
*
|
|
*/
|
|
|
|
#ifndef CEPH_COMMON_PERF_HISTOGRAM_H
|
|
#define CEPH_COMMON_PERF_HISTOGRAM_H
|
|
|
|
#include "common/Formatter.h"
|
|
#include "include/int_types.h"
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <memory>
|
|
|
|
#include "include/ceph_assert.h"
|
|
|
|
class PerfHistogramCommon {
|
|
public:
|
|
enum scale_type_d : uint8_t {
|
|
SCALE_LINEAR = 1,
|
|
SCALE_LOG2 = 2,
|
|
};
|
|
|
|
struct axis_config_d {
|
|
const char *m_name = nullptr;
|
|
scale_type_d m_scale_type = SCALE_LINEAR;
|
|
int64_t m_min = 0;
|
|
int64_t m_quant_size = 0;
|
|
int32_t m_buckets = 0;
|
|
axis_config_d() = default;
|
|
axis_config_d(const char* name,
|
|
scale_type_d scale_type,
|
|
int64_t min,
|
|
int64_t quant_size,
|
|
int32_t buckets)
|
|
: m_name(name),
|
|
m_scale_type(scale_type),
|
|
m_min(min),
|
|
m_quant_size(quant_size),
|
|
m_buckets(buckets)
|
|
{}
|
|
};
|
|
|
|
protected:
|
|
/// Dump configuration of one axis to a formatter
|
|
static void dump_formatted_axis(ceph::Formatter *f, const axis_config_d &ac);
|
|
|
|
/// Quantize given value and convert to bucket number on given axis
|
|
static int64_t get_bucket_for_axis(int64_t value, const axis_config_d &ac);
|
|
|
|
/// Calculate inclusive ranges of axis values for each bucket on that axis
|
|
static std::vector<std::pair<int64_t, int64_t>> get_axis_bucket_ranges(
|
|
const axis_config_d &ac);
|
|
};
|
|
|
|
/// PerfHistogram does trace a histogram of input values. It's an extended
|
|
/// version of a standard histogram which does trace characteristics of a single
|
|
/// one value only. In this implementation, values can be traced in multiple
|
|
/// dimensions - i.e. we can create a histogram of input request size (first
|
|
/// dimension) and processing latency (second dimension). Creating standard
|
|
/// histogram out of such multidimensional one is trivial and requires summing
|
|
/// values across dimensions we're not interested in.
|
|
template <int DIM = 2>
|
|
class PerfHistogram : public PerfHistogramCommon {
|
|
public:
|
|
/// Initialize new histogram object
|
|
PerfHistogram(std::initializer_list<axis_config_d> axes_config) {
|
|
ceph_assert(axes_config.size() == DIM &&
|
|
"Invalid number of axis configuration objects");
|
|
|
|
int i = 0;
|
|
for (const auto &ac : axes_config) {
|
|
ceph_assertf(ac.m_buckets > 0,
|
|
"Must have at least one bucket on axis");
|
|
ceph_assertf(ac.m_quant_size > 0,
|
|
"Quantization unit must be non-zero positive integer value");
|
|
|
|
m_axes_config[i++] = ac;
|
|
}
|
|
|
|
m_rawData.reset(new std::atomic<uint64_t>[get_raw_size()]);
|
|
}
|
|
|
|
/// Copy from other histogram object
|
|
PerfHistogram(const PerfHistogram &other)
|
|
: m_axes_config(other.m_axes_config) {
|
|
int64_t size = get_raw_size();
|
|
m_rawData.reset(new std::atomic<uint64_t>[size]);
|
|
for (int64_t i = 0; i < size; i++) {
|
|
m_rawData[i] = other.m_rawData[i];
|
|
}
|
|
}
|
|
|
|
/// Set all histogram values to 0
|
|
void reset() {
|
|
auto size = get_raw_size();
|
|
for (auto i = size; --i >= 0;) {
|
|
m_rawData[i] = 0;
|
|
}
|
|
}
|
|
|
|
/// Increase counter for given axis values by one
|
|
template <typename... T>
|
|
void inc(T... axis) {
|
|
auto index = get_raw_index_for_value(axis...);
|
|
m_rawData[index] += 1;
|
|
}
|
|
|
|
/// Increase counter for given axis buckets by one
|
|
template <typename... T>
|
|
void inc_bucket(T... bucket) {
|
|
auto index = get_raw_index_for_bucket(bucket...);
|
|
m_rawData[index] += 1;
|
|
}
|
|
|
|
/// Read value from given bucket
|
|
template <typename... T>
|
|
uint64_t read_bucket(T... bucket) const {
|
|
auto index = get_raw_index_for_bucket(bucket...);
|
|
return m_rawData[index];
|
|
}
|
|
|
|
/// Dump data to a Formatter object
|
|
void dump_formatted(ceph::Formatter *f) const {
|
|
// Dump axes configuration
|
|
f->open_array_section("axes");
|
|
for (auto &ac : m_axes_config) {
|
|
dump_formatted_axis(f, ac);
|
|
}
|
|
f->close_section();
|
|
|
|
// Dump histogram values
|
|
dump_formatted_values(f);
|
|
}
|
|
|
|
protected:
|
|
/// Raw data stored as linear space, internal indexes are calculated on
|
|
/// demand.
|
|
std::unique_ptr<std::atomic<uint64_t>[]> m_rawData;
|
|
|
|
/// Configuration of axes
|
|
std::array<axis_config_d, DIM> m_axes_config;
|
|
|
|
/// Dump histogram counters to a formatter
|
|
void dump_formatted_values(ceph::Formatter *f) const {
|
|
visit_values([f](int) { f->open_array_section("values"); },
|
|
[f](int64_t value) { f->dump_unsigned("value", value); },
|
|
[f](int) { f->close_section(); });
|
|
}
|
|
|
|
/// Get number of all histogram counters
|
|
int64_t get_raw_size() {
|
|
int64_t ret = 1;
|
|
for (const auto &ac : m_axes_config) {
|
|
ret *= ac.m_buckets;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// Calculate m_rawData index from axis values
|
|
template <typename... T>
|
|
int64_t get_raw_index_for_value(T... axes) const {
|
|
static_assert(sizeof...(T) == DIM, "Incorrect number of arguments");
|
|
return get_raw_index_internal<0>(get_bucket_for_axis, 0, axes...);
|
|
}
|
|
|
|
/// Calculate m_rawData index from axis bucket numbers
|
|
template <typename... T>
|
|
int64_t get_raw_index_for_bucket(T... buckets) const {
|
|
static_assert(sizeof...(T) == DIM, "Incorrect number of arguments");
|
|
return get_raw_index_internal<0>(
|
|
[](int64_t bucket, const axis_config_d &ac) {
|
|
ceph_assertf(bucket >= 0, "Bucket index can not be negative");
|
|
ceph_assertf(bucket < ac.m_buckets, "Bucket index too large");
|
|
return bucket;
|
|
},
|
|
0, buckets...);
|
|
}
|
|
|
|
template <int level = 0, typename F, typename... T>
|
|
int64_t get_raw_index_internal(F bucket_evaluator, int64_t startIndex,
|
|
int64_t value, T... tail) const {
|
|
static_assert(level + 1 + sizeof...(T) == DIM,
|
|
"Internal consistency check");
|
|
auto &ac = m_axes_config[level];
|
|
auto bucket = bucket_evaluator(value, ac);
|
|
return get_raw_index_internal<level + 1>(
|
|
bucket_evaluator, ac.m_buckets * startIndex + bucket, tail...);
|
|
}
|
|
|
|
template <int level, typename F>
|
|
int64_t get_raw_index_internal(F, int64_t startIndex) const {
|
|
static_assert(level == DIM, "Internal consistency check");
|
|
return startIndex;
|
|
}
|
|
|
|
/// Visit all histogram counters, call onDimensionEnter / onDimensionLeave
|
|
/// when starting / finishing traversal
|
|
/// on given axis, call onValue when dumping raw histogram counter value.
|
|
template <typename FDE, typename FV, typename FDL>
|
|
void visit_values(FDE onDimensionEnter, FV onValue, FDL onDimensionLeave,
|
|
int level = 0, int startIndex = 0) const {
|
|
if (level == DIM) {
|
|
onValue(m_rawData[startIndex]);
|
|
return;
|
|
}
|
|
|
|
onDimensionEnter(level);
|
|
auto &ac = m_axes_config[level];
|
|
startIndex *= ac.m_buckets;
|
|
for (int32_t i = 0; i < ac.m_buckets; ++i, ++startIndex) {
|
|
visit_values(onDimensionEnter, onValue, onDimensionLeave, level + 1,
|
|
startIndex);
|
|
}
|
|
onDimensionLeave(level);
|
|
}
|
|
};
|
|
|
|
#endif
|