aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/monlib/metrics/timer.h
blob: 5c4e26e37bf912000415ce812ebc20e74d065590 (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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#pragma once

#include "metric.h"

#include <util/generic/typetraits.h>

#include <chrono>


namespace NMonitoring {

    /**
     * A timing scope to record elapsed time since creation.
     */
    template <typename TMetric,
              typename Resolution = std::chrono::milliseconds,
              typename Clock = std::chrono::high_resolution_clock>
    class TMetricTimerScope {
    public:
        explicit TMetricTimerScope(TMetric* metric)
                : Metric_(metric)
                , StartTime_(Clock::now())
        {
            Y_ENSURE(Metric_);
        }

        TMetricTimerScope(TMetricTimerScope&) = delete;
        TMetricTimerScope& operator=(const TMetricTimerScope&) = delete;

        TMetricTimerScope(TMetricTimerScope&& other) {
            *this = std::move(other);
        }

        TMetricTimerScope& operator=(TMetricTimerScope&& other) {
            Metric_ = other.Metric_;
            other.Metric_ = nullptr;
            StartTime_ = std::move(other.StartTime_);

            return *this;
        }

        void Record() {
            Y_VERIFY_DEBUG(Metric_);
            if (Metric_ == nullptr) {
                return;
            }

            auto duration = std::chrono::duration_cast<Resolution>(Clock::now() - StartTime_).count();
            if constexpr (std::is_same<TMetric, TGauge>::value) {
                Metric_->Set(duration);
            } else if constexpr (std::is_same<TMetric, TIntGauge>::value) {
                Metric_->Set(duration);
            } else if constexpr (std::is_same<TMetric, TCounter>::value) {
                Metric_->Add(duration);
            } else if constexpr (std::is_same<TMetric, TRate>::value) {
                Metric_->Add(duration);
            } else if constexpr (std::is_same<TMetric, THistogram>::value) {
                Metric_->Record(duration);
            } else {
                static_assert(TDependentFalse<TMetric>, "Not supported metric type");
            }

            Metric_ = nullptr;
        }

        ~TMetricTimerScope() {
            if (Metric_ == nullptr) {
                return;
            }

            Record();
        }

    private:
        TMetric* Metric_{nullptr};
        typename Clock::time_point StartTime_;
    };

    /**
     * @brief A class that is supposed to use to measure execution time of an asynchronuous operation.
     *
     * In order to be able to capture an object into a lambda which is then passed to TFuture::Subscribe/Apply,
     * the object must be copy constructible (limitation of the std::function class). So, we cannot use the TMetricTimerScope
     * with the abovementioned functions without storing it in a shared pointer or somewhere else. This class works around this
     * issue with wrapping the timer with a auto_ptr-like hack  Also, Record is const so that one doesn't need to make every lambda mutable
     * just to record time measurement.
     */
    template <typename TMetric,
              typename Resolution = std::chrono::milliseconds,
              typename Clock = std::chrono::high_resolution_clock>
    class TFutureFriendlyTimer {
    public:
        explicit TFutureFriendlyTimer(TMetric* metric)
            : Impl_{metric}
        {
        }

        TFutureFriendlyTimer(const TFutureFriendlyTimer& other)
            : Impl_{std::move(other.Impl_)}
        {
        }

        TFutureFriendlyTimer& operator=(const TFutureFriendlyTimer& other) {
            Impl_ = std::move(other.Impl_);
        }

        TFutureFriendlyTimer(TFutureFriendlyTimer&&) = default;
        TFutureFriendlyTimer& operator=(TFutureFriendlyTimer&& other) = default;

        void Record() const {
            Impl_.Record();
        }

    private:
        mutable TMetricTimerScope<TMetric, Resolution, Clock> Impl_;
    };

    template <typename TMetric>
    TMetricTimerScope<TMetric> ScopeTimer(TMetric* metric) {
        return TMetricTimerScope<TMetric>{metric};
    }

    template <typename TMetric>
    TFutureFriendlyTimer<TMetric> FutureTimer(TMetric* metric) {
        return TFutureFriendlyTimer<TMetric>{metric};
    }
}