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