xref: /openbsd-src/gnu/llvm/llvm/tools/llvm-exegesis/lib/ProgressMeter.h (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
1*d415bd75Srobert //===-- ProgressMeter.h -----------------------------------------*- C++ -*-===//
2*d415bd75Srobert //
3*d415bd75Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*d415bd75Srobert // See https://llvm.org/LICENSE.txt for license information.
5*d415bd75Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*d415bd75Srobert //
7*d415bd75Srobert //===----------------------------------------------------------------------===//
8*d415bd75Srobert 
9*d415bd75Srobert #ifndef LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H
10*d415bd75Srobert #define LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H
11*d415bd75Srobert 
12*d415bd75Srobert #include "llvm/Support/Format.h"
13*d415bd75Srobert #include "llvm/Support/raw_ostream.h"
14*d415bd75Srobert #include <cassert>
15*d415bd75Srobert #include <chrono>
16*d415bd75Srobert #include <cmath>
17*d415bd75Srobert #include <optional>
18*d415bd75Srobert #include <type_traits>
19*d415bd75Srobert 
20*d415bd75Srobert namespace llvm {
21*d415bd75Srobert namespace exegesis {
22*d415bd75Srobert 
23*d415bd75Srobert /// Represents `\sum_{i=1..accumulated}{step_i} / accumulated`,
24*d415bd75Srobert /// where `step_i` is the value passed to the `i`-th call to `step()`,
25*d415bd75Srobert /// and `accumulated` is the total number of calls to `step()`.
26*d415bd75Srobert template <typename NumTy, typename DenTy = int> class SimpleMovingAverage {
27*d415bd75Srobert   NumTy Accumulated = NumTy(0);
28*d415bd75Srobert   DenTy Steps = 0;
29*d415bd75Srobert 
30*d415bd75Srobert public:
31*d415bd75Srobert   SimpleMovingAverage() = default;
32*d415bd75Srobert 
33*d415bd75Srobert   SimpleMovingAverage(const SimpleMovingAverage &) = delete;
34*d415bd75Srobert   SimpleMovingAverage(SimpleMovingAverage &&) = delete;
35*d415bd75Srobert   SimpleMovingAverage &operator=(const SimpleMovingAverage &) = delete;
36*d415bd75Srobert   SimpleMovingAverage &operator=(SimpleMovingAverage &&) = delete;
37*d415bd75Srobert 
step(NumTy Quantity)38*d415bd75Srobert   inline void step(NumTy Quantity) {
39*d415bd75Srobert     Accumulated += Quantity;
40*d415bd75Srobert     ++Steps;
41*d415bd75Srobert   }
42*d415bd75Srobert 
getAccumulated()43*d415bd75Srobert   inline NumTy getAccumulated() const { return Accumulated; }
44*d415bd75Srobert 
getNumSteps()45*d415bd75Srobert   inline DenTy getNumSteps() const { return Steps; }
46*d415bd75Srobert 
47*d415bd75Srobert   template <typename AvgTy = NumTy>
getAverage()48*d415bd75Srobert   inline std::optional<AvgTy> getAverage() const {
49*d415bd75Srobert     if (Steps == 0)
50*d415bd75Srobert       return std::nullopt;
51*d415bd75Srobert     return AvgTy(Accumulated) / Steps;
52*d415bd75Srobert   }
53*d415bd75Srobert };
54*d415bd75Srobert 
55*d415bd75Srobert template <typename ClockTypeTy = std::chrono::steady_clock,
56*d415bd75Srobert           typename = std::enable_if_t<ClockTypeTy::is_steady>>
57*d415bd75Srobert class ProgressMeter {
58*d415bd75Srobert public:
59*d415bd75Srobert   using ClockType = ClockTypeTy;
60*d415bd75Srobert   using TimePointType = std::chrono::time_point<ClockType>;
61*d415bd75Srobert   using DurationType = std::chrono::duration<typename ClockType::rep,
62*d415bd75Srobert                                              typename ClockType::period>;
63*d415bd75Srobert   using CompetionPercentage = int;
64*d415bd75Srobert   using Sec = std::chrono::duration<double, std::chrono::seconds::period>;
65*d415bd75Srobert 
66*d415bd75Srobert private:
67*d415bd75Srobert   raw_ostream &Out;
68*d415bd75Srobert   const int NumStepsTotal;
69*d415bd75Srobert   SimpleMovingAverage<DurationType> ElapsedTotal;
70*d415bd75Srobert 
71*d415bd75Srobert public:
72*d415bd75Srobert   friend class ProgressMeterStep;
73*d415bd75Srobert   class ProgressMeterStep {
74*d415bd75Srobert     ProgressMeter *P;
75*d415bd75Srobert     const TimePointType Begin;
76*d415bd75Srobert 
77*d415bd75Srobert   public:
ProgressMeterStep(ProgressMeter * P_)78*d415bd75Srobert     inline ProgressMeterStep(ProgressMeter *P_)
79*d415bd75Srobert         : P(P_), Begin(P ? ProgressMeter<ClockType>::ClockType::now()
80*d415bd75Srobert                          : TimePointType()) {}
81*d415bd75Srobert 
~ProgressMeterStep()82*d415bd75Srobert     inline ~ProgressMeterStep() {
83*d415bd75Srobert       if (!P)
84*d415bd75Srobert         return;
85*d415bd75Srobert       const TimePointType End = ProgressMeter<ClockType>::ClockType::now();
86*d415bd75Srobert       P->step(End - Begin);
87*d415bd75Srobert     }
88*d415bd75Srobert 
89*d415bd75Srobert     ProgressMeterStep(const ProgressMeterStep &) = delete;
90*d415bd75Srobert     ProgressMeterStep(ProgressMeterStep &&) = delete;
91*d415bd75Srobert     ProgressMeterStep &operator=(const ProgressMeterStep &) = delete;
92*d415bd75Srobert     ProgressMeterStep &operator=(ProgressMeterStep &&) = delete;
93*d415bd75Srobert   };
94*d415bd75Srobert 
95*d415bd75Srobert   ProgressMeter(int NumStepsTotal_, raw_ostream &out_ = llvm::errs())
Out(out_)96*d415bd75Srobert       : Out(out_), NumStepsTotal(NumStepsTotal_) {
97*d415bd75Srobert     assert(NumStepsTotal > 0 && "No steps are planned?");
98*d415bd75Srobert   }
99*d415bd75Srobert 
100*d415bd75Srobert   ProgressMeter(const ProgressMeter &) = delete;
101*d415bd75Srobert   ProgressMeter(ProgressMeter &&) = delete;
102*d415bd75Srobert   ProgressMeter &operator=(const ProgressMeter &) = delete;
103*d415bd75Srobert   ProgressMeter &operator=(ProgressMeter &&) = delete;
104*d415bd75Srobert 
105*d415bd75Srobert private:
step(DurationType Elapsed)106*d415bd75Srobert   void step(DurationType Elapsed) {
107*d415bd75Srobert     assert((ElapsedTotal.getNumSteps() < NumStepsTotal) && "Step overflow!");
108*d415bd75Srobert     assert(Elapsed.count() >= 0 && "Negative time drift detected.");
109*d415bd75Srobert 
110*d415bd75Srobert     auto [OldProgress, OldEta] = eta();
111*d415bd75Srobert     ElapsedTotal.step(Elapsed);
112*d415bd75Srobert     auto [NewProgress, NewEta] = eta();
113*d415bd75Srobert 
114*d415bd75Srobert     if (NewProgress < OldProgress + 1)
115*d415bd75Srobert       return;
116*d415bd75Srobert 
117*d415bd75Srobert     Out << format("Processing... %*d%%", 3, NewProgress);
118*d415bd75Srobert     if (NewEta) {
119*d415bd75Srobert       int SecondsTotal = std::ceil(NewEta->count());
120*d415bd75Srobert       int Seconds = SecondsTotal % 60;
121*d415bd75Srobert       int MinutesTotal = SecondsTotal / 60;
122*d415bd75Srobert 
123*d415bd75Srobert       Out << format(", ETA %02d:%02d", MinutesTotal, Seconds);
124*d415bd75Srobert     }
125*d415bd75Srobert     Out << "\n";
126*d415bd75Srobert     Out.flush();
127*d415bd75Srobert   }
128*d415bd75Srobert 
eta()129*d415bd75Srobert   inline std::pair<CompetionPercentage, std::optional<Sec>> eta() const {
130*d415bd75Srobert     CompetionPercentage Progress =
131*d415bd75Srobert         (100 * ElapsedTotal.getNumSteps()) / NumStepsTotal;
132*d415bd75Srobert 
133*d415bd75Srobert     std::optional<Sec> ETA;
134*d415bd75Srobert     if (std::optional<Sec> AverageStepDuration =
135*d415bd75Srobert             ElapsedTotal.template getAverage<Sec>())
136*d415bd75Srobert       ETA = (NumStepsTotal - ElapsedTotal.getNumSteps()) * *AverageStepDuration;
137*d415bd75Srobert 
138*d415bd75Srobert     return {Progress, ETA};
139*d415bd75Srobert   }
140*d415bd75Srobert };
141*d415bd75Srobert 
142*d415bd75Srobert } // namespace exegesis
143*d415bd75Srobert } // namespace llvm
144*d415bd75Srobert 
145*d415bd75Srobert #endif
146