aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/cpulimit/cpu.cpp
blob: 583b38055ed238f2830b5e5b23cc9807f9cb6966 (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
#include "cpu.h"

#include <util/datetime/cputimer.h>
#include <util/generic/yexception.h>
#include <util/generic/ylimits.h>
#include <util/random/random.h>
#include <util/system/rusage.h>
#include <util/thread/pool.h>

namespace {
    TDuration GetUsage() {
        const auto rusage = TRusage::Get();
        return rusage.Utime + rusage.Stime;
    }

    TInstant MonotonicNow() {
        return TInstant::Zero() + CyclesToDuration(GetCycleCount());
    }
}

namespace NCpuLimit {
    TCpuMeasurer::TCpuMeasurer(TDuration probePeriod)
        : FastProbe_{.Period = probePeriod, .Usage = 0}
        , SlowProbe_{.Period = TDuration::MilliSeconds(100), .Usage = 0}
    {
        const auto now = MonotonicNow();
        const auto usageNow = GetUsage();
        for (auto [probe, size] : {std::pair(&FastProbe_, 5), std::pair(&SlowProbe_, 3)}) {
            probe->Window.assign(size, {now, usageNow});
            probe->MeasurerThread = SystemThreadFactory()->Run([this, probe = probe] {
                UpdateProbeThread(*probe);
            });
        }
    }

    TCpuMeasurer::~TCpuMeasurer() {
        Finished_ = true;
        for (auto* probe : {&FastProbe_, &SlowProbe_}) {
            probe->MeasurerThread->Join();
        }
    }

    void TCpuMeasurer::UpdateProbeThread(TProbe& probe) {
        const ui64 numberOfMeasurments = 10;
        const TDuration checkPeriod = probe.Period / numberOfMeasurments;
        while (!Finished_.load()) {
            Sleep(checkPeriod);

            const auto now = MonotonicNow();
            const auto usageNow = GetUsage();

            const auto [windowStartTime, windowStartUsage] = probe.Window.front();

            const auto windowDuration = now - windowStartTime;
            const auto windowUsage = usageNow - windowStartUsage;

            probe.Usage.store(windowUsage / windowDuration);
            probe.Window.emplace_back(now, usageNow);

            if (probe.Window.size() > numberOfMeasurments) {
                probe.Window.pop_front();
            }
        }
    }

    TCpuLimiter::TCpuLimiter(double slowThreshold, double fastThresholdBegin, double fastThresholdEnd)
        : SlowThreshold_(slowThreshold)
        , FastThresholdBegin_(fastThresholdBegin)
        , FastThresholdEnd_(fastThresholdEnd)
    {
        Y_ENSURE(FastThresholdBegin_ <= FastThresholdEnd_);
    }

    bool TCpuLimiter::ThrottleHard(double slowUsage, double fastUsage) const {
        if (slowUsage <= SlowThreshold_) {
            return false;
        }
        if (fastUsage > FastThresholdEnd_) {
            return true;
        } else if (fastUsage > FastThresholdBegin_) {
            return RandomNumber<ui32>() * (FastThresholdEnd_ - FastThresholdBegin_) <
                   Max<ui32>() * (fastUsage - FastThresholdBegin_);
        } else {
            return false;
        }
    }

    bool TCpuLimiter::ThrottleSoft(double slowUsage, double fastUsage) const {
        return slowUsage > SlowThreshold_ && fastUsage > FastThresholdEnd_;
    }
}