#pragma once #include "histogram.h" #include <util/generic/scope.h> #include <chrono> namespace NMonitoring { /** * A timer counter which aggregates timing durations and provides duration * statistics in selected time resolution. */ template <typename TResolution> class TTimerImpl { public: /** * Construct a timer given the Lowest and Highest values to be tracked * and a number of significant decimal digits. Providing a * lowestDiscernibleValue is useful in situations where the units used for * the timer's values are much smaller that the minimal accuracy * required. E.g. when tracking time values stated in nanosecond units, * where the minimal accuracy required is a microsecond, the proper value * for lowestDiscernibleValue would be 1000. * * @param min The lowest value that can be discerned (distinguished from * 0) by the timer. Must be a positive integer that is >= 1. * May be internally rounded down to nearest power of 2. * * @param max The highest value to be tracked by the timer. Must be a * positive integer that is >= (2 * min). * * @param numberOfSignificantValueDigits Specifies the precision to use. * This is the number of significant decimal digits to which the * timer will maintain value resolution and separation. Must be * a non-negative integer between 0 and 5. */ TTimerImpl(ui64 min, ui64 max, i32 numberOfSignificantValueDigits = 3) : TTimerImpl(TResolution(min), TResolution(max), numberOfSignificantValueDigits) { } /** * Construct a timer given the Lowest and Highest values to be tracked * and a number of significant decimal digits. * * @param min The lowest value that can be discerned (distinguished from * 0) by the timer. * * @param max The highest value to be tracked by the histogram. Must be a * positive integer that is >= (2 * min). * * @param numberOfSignificantValueDigits Specifies the precision to use. */ template <typename TDurationMin, typename TDurationMax> TTimerImpl(TDurationMin min, TDurationMax max, i32 numberOfSignificantValueDigits = 3) : Histogram_(std::chrono::duration_cast<TResolution>(min).count(), std::chrono::duration_cast<TResolution>(max).count(), numberOfSignificantValueDigits) { } /** * Records a value in the timer with timer resulution. Recorded value will * be rounded to a precision at or better than the * NumberOfSignificantValueDigits specified at construction time. * * @param duration duration to add to the timer * @return false if the value is larger than the max and can't be recorded, * true otherwise. */ bool RecordValue(ui64 duration) { return Histogram_.RecordValue(duration); } /** * Records a duration in the timer. Recorded value will be converted to * the timer resulution and rounded to a precision at or better than the * NumberOfSignificantValueDigits specified at construction time. * * @param duration duration to add to the timer * @return false if the value is larger than the max and can't be recorded, * true otherwise. */ template <typename TDuration> bool RecordValue(TDuration duration) { auto count = static_cast<ui64>( std::chrono::duration_cast<TResolution>(duration).count()); return RecordValue(count); } /** * Records count values in the timer with timer resulution. Recorded value will * be rounded to a precision at or better than the * NumberOfSignificantValueDigits specified at construction time. * * @param duration duration to add to the timer * @param count number of values to add to the histogram * @return false if the value is larger than the max and can't be recorded, * true otherwise. */ bool RecordValues(ui64 duration, ui64 count) { return Histogram_.RecordValues(duration, count); } /** * Measures a time of functor execution. * * @param fn functor whose duration should be timed */ template <typename TFunc> void Measure(TFunc&& fn) { using TClock = std::chrono::high_resolution_clock; auto start = TClock::now(); Y_SCOPE_EXIT(this, start) { RecordValue(TClock::now() - start); }; fn(); } /** * Place a copy of the value counts accumulated since the last snapshot * was taken into {@code snapshot}. Calling this member-function will * reset the value counts, and start accumulating value counts for the * next interval. * * @param snapshot the structure into which the values should be copied. */ void TakeSnapshot(THistogramSnapshot* snapshot) { Histogram_.TakeSnaphot(snapshot); } private: THdrHistogram Histogram_; }; /** * Timer template instantiations for certain time resolutions. */ using TTimerNs = TTimerImpl<std::chrono::nanoseconds>; using TTimerUs = TTimerImpl<std::chrono::microseconds>; using TTimerMs = TTimerImpl<std::chrono::milliseconds>; using TTimerS = TTimerImpl<std::chrono::seconds>; /** * A timing scope to record elapsed time since creation. */ template <typename TTimer, typename TFunc = std::function<void(std::chrono::high_resolution_clock::duration)>> class TTimerScope { using TClock = std::chrono::high_resolution_clock; public: explicit TTimerScope(TTimer* timer, TFunc* callback = nullptr) : Timer_(timer) , StartTime_(TClock::now()) , Callback_(callback) { } ~TTimerScope() { TClock::duration duration = TClock::now() - StartTime_; if (Callback_) { (*Callback_)(duration); } Timer_->RecordValue(duration); } private: TTimer* Timer_; TClock::time_point StartTime_; TFunc* Callback_; }; }