summaryrefslogtreecommitdiffstats
path: root/ydb/core/load_test/quantile.h
blob: 8f403a727b98e06729988f6d13df38911389fca2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#pragma once

#include "defs.h"
#include "time_series.h"

#include <library/cpp/monlib/dynamic_counters/counters.h>

namespace NKikimr {

    template<typename T>
    class TQuantileTracker : public TTimeSeries<T>
    {
        using TItem = typename TTimeSeries<T>::TItem;
        using TTimeSeries<T>::Items;

        using TPercentile = std::pair<float, ::NMonitoring::TDynamicCounters::TCounterPtr>;
        using TPercentiles = TVector<TPercentile>;

        TPercentiles Percentiles;
        TIntrusivePtr<::NMonitoring::TDynamicCounters> Counters;
        ::NMonitoring::TDynamicCounters::TCounterPtr Samples;

        struct TCompareTimestamp {
            bool operator ()(const TItem& x, TInstant y) {
                return x.Timestamp < y;
            }
        };

    public:
        using TTimeSeries<T>::TTimeSeries;

        TQuantileTracker(TDuration lifetime, TIntrusivePtr<::NMonitoring::TDynamicCounters> counters,
                const TString& metric, const TVector<float>& percentiles)
            : TTimeSeries<T>(lifetime)
            , Counters(counters)
        {
            Y_VERIFY(Counters);
            Samples = Counters->GetCounter("samples", false);
            for (auto perc : percentiles) {
                auto subgroup = Counters->GetSubgroup("percentile", Sprintf("%.1f", perc * 100.f));
                Percentiles.emplace_back(perc, subgroup->GetCounter(metric, false));
            }
        }

        void CalculateQuantiles() const {
            Y_VERIFY(Counters);

            *Samples = Items.size();

            if (Items.empty()) {
                for (auto& perc : Percentiles) {
                    *perc.second = T();
                }
                return;
            }

            // create a vector of values matching time criterion
            TVector<T> values;
            values.reserve(Items.size());
            for (const TItem &item : Items) {
                values.push_back(item.Value);
            }

            // sort and calculate quantiles
            std::sort(values.begin(), values.end());
            const size_t maxIndex = values.size() - 1;
            for (auto& perc : Percentiles) {
                const size_t index = Min<size_t>(maxIndex, maxIndex  * perc.first);
                *perc.second = values[index];
            }
        }

        bool CalculateQuantiles(size_t count, const size_t *numerators, size_t denominator, T *res,
                size_t *numSamples = nullptr, TDuration *interval = nullptr) const {
            if (numSamples) {
                *numSamples = Items.size();
                if (interval) {
                    *interval = Items ? Items.back().Timestamp - Items.front().Timestamp : TDuration::Zero();
                }
            }

            // create a vector of values matching time criterion
            TVector<T> values;
            values.reserve(Items.size());
            for (const TItem &item : Items) {
                values.push_back(item.Value);
            }

            // if there are no values, return false meaning we can't get adequate results
            if (values.empty()) {
                std::fill(res, res + count, T());
                return false;
            }

            // sort and calculate quantiles
            std::sort(values.begin(), values.end());
            size_t maxIndex = values.size() - 1;
            while (count--) {
                const size_t numerator = *numerators++;
                Y_VERIFY(numerator >= 0 && numerator <= denominator);
                const size_t index = maxIndex * numerator / denominator;
                *res++ = values[index];
            }
            return true;
        }
    };

} // NKikimr