1 //===-- ProgressMeter.h -----------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H
10 #define LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H
12 #include "llvm/Support/Format.h"
13 #include "llvm/Support/raw_ostream.h"
18 #include <type_traits>
23 /// Represents `\sum_{i=1..accumulated}{step_i} / accumulated`,
24 /// where `step_i` is the value passed to the `i`-th call to `step()`,
25 /// and `accumulated` is the total number of calls to `step()`.
26 template <typename NumTy
, typename DenTy
= int> class SimpleMovingAverage
{
27 NumTy Accumulated
= NumTy(0);
31 SimpleMovingAverage() = default;
33 SimpleMovingAverage(const SimpleMovingAverage
&) = delete;
34 SimpleMovingAverage(SimpleMovingAverage
&&) = delete;
35 SimpleMovingAverage
&operator=(const SimpleMovingAverage
&) = delete;
36 SimpleMovingAverage
&operator=(SimpleMovingAverage
&&) = delete;
38 inline void step(NumTy Quantity
) {
39 Accumulated
+= Quantity
;
43 inline NumTy
getAccumulated() const { return Accumulated
; }
45 inline DenTy
getNumSteps() const { return Steps
; }
47 template <typename AvgTy
= NumTy
>
48 inline std::optional
<AvgTy
> getAverage() const {
51 return AvgTy(Accumulated
) / Steps
;
55 template <typename ClockTypeTy
= std::chrono::steady_clock
,
56 typename
= std::enable_if_t
<ClockTypeTy::is_steady
>>
59 using ClockType
= ClockTypeTy
;
60 using TimePointType
= std::chrono::time_point
<ClockType
>;
61 using DurationType
= std::chrono::duration
<typename
ClockType::rep
,
62 typename
ClockType::period
>;
63 using CompetionPercentage
= int;
64 using Sec
= std::chrono::duration
<double, std::chrono::seconds::period
>;
68 const int NumStepsTotal
;
69 SimpleMovingAverage
<DurationType
> ElapsedTotal
;
72 friend class ProgressMeterStep
;
73 class ProgressMeterStep
{
75 const TimePointType Begin
;
78 inline ProgressMeterStep(ProgressMeter
*P_
)
79 : P(P_
), Begin(P
? ProgressMeter
<ClockType
>::ClockType::now()
82 inline ~ProgressMeterStep() {
85 const TimePointType End
= ProgressMeter
<ClockType
>::ClockType::now();
89 ProgressMeterStep(const ProgressMeterStep
&) = delete;
90 ProgressMeterStep(ProgressMeterStep
&&) = delete;
91 ProgressMeterStep
&operator=(const ProgressMeterStep
&) = delete;
92 ProgressMeterStep
&operator=(ProgressMeterStep
&&) = delete;
95 ProgressMeter(int NumStepsTotal_
, raw_ostream
&out_
= errs())
96 : Out(out_
), NumStepsTotal(NumStepsTotal_
) {
97 assert(NumStepsTotal
> 0 && "No steps are planned?");
100 ProgressMeter(const ProgressMeter
&) = delete;
101 ProgressMeter(ProgressMeter
&&) = delete;
102 ProgressMeter
&operator=(const ProgressMeter
&) = delete;
103 ProgressMeter
&operator=(ProgressMeter
&&) = delete;
106 void step(DurationType Elapsed
) {
107 assert((ElapsedTotal
.getNumSteps() < NumStepsTotal
) && "Step overflow!");
108 assert(Elapsed
.count() >= 0 && "Negative time drift detected.");
110 auto [OldProgress
, OldEta
] = eta();
111 ElapsedTotal
.step(Elapsed
);
112 auto [NewProgress
, NewEta
] = eta();
114 if (NewProgress
< OldProgress
+ 1)
117 Out
<< format("Processing... %*d%%", 3, NewProgress
);
119 int SecondsTotal
= std::ceil(NewEta
->count());
120 int Seconds
= SecondsTotal
% 60;
121 int MinutesTotal
= SecondsTotal
/ 60;
123 Out
<< format(", ETA %02d:%02d", MinutesTotal
, Seconds
);
129 inline std::pair
<CompetionPercentage
, std::optional
<Sec
>> eta() const {
130 CompetionPercentage Progress
=
131 (100 * ElapsedTotal
.getNumSteps()) / NumStepsTotal
;
133 std::optional
<Sec
> ETA
;
134 if (std::optional
<Sec
> AverageStepDuration
=
135 ElapsedTotal
.template getAverage
<Sec
>())
136 ETA
= (NumStepsTotal
- ElapsedTotal
.getNumSteps()) * *AverageStepDuration
;
138 return {Progress
, ETA
};
142 } // namespace exegesis