1 //===-- ProgressMeter.h -----------------------------------------*- C++ -*-===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H 10 #define LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H 11 12 #include "llvm/Support/Format.h" 13 #include "llvm/Support/raw_ostream.h" 14 #include <cassert> 15 #include <chrono> 16 #include <cmath> 17 #include <optional> 18 #include <type_traits> 19 20 namespace llvm { 21 namespace exegesis { 22 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); 28 DenTy Steps = 0; 29 30 public: 31 SimpleMovingAverage() = default; 32 33 SimpleMovingAverage(const SimpleMovingAverage &) = delete; 34 SimpleMovingAverage(SimpleMovingAverage &&) = delete; 35 SimpleMovingAverage &operator=(const SimpleMovingAverage &) = delete; 36 SimpleMovingAverage &operator=(SimpleMovingAverage &&) = delete; 37 step(NumTy Quantity)38 inline void step(NumTy Quantity) { 39 Accumulated += Quantity; 40 ++Steps; 41 } 42 getAccumulated()43 inline NumTy getAccumulated() const { return Accumulated; } 44 getNumSteps()45 inline DenTy getNumSteps() const { return Steps; } 46 47 template <typename AvgTy = NumTy> getAverage()48 inline std::optional<AvgTy> getAverage() const { 49 if (Steps == 0) 50 return std::nullopt; 51 return AvgTy(Accumulated) / Steps; 52 } 53 }; 54 55 template <typename ClockTypeTy = std::chrono::steady_clock, 56 typename = std::enable_if_t<ClockTypeTy::is_steady>> 57 class ProgressMeter { 58 public: 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>; 65 66 private: 67 raw_ostream &Out; 68 const int NumStepsTotal; 69 SimpleMovingAverage<DurationType> ElapsedTotal; 70 71 public: 72 friend class ProgressMeterStep; 73 class ProgressMeterStep { 74 ProgressMeter *P; 75 const TimePointType Begin; 76 77 public: ProgressMeterStep(ProgressMeter * P_)78 inline ProgressMeterStep(ProgressMeter *P_) 79 : P(P_), Begin(P ? ProgressMeter<ClockType>::ClockType::now() 80 : TimePointType()) {} 81 ~ProgressMeterStep()82 inline ~ProgressMeterStep() { 83 if (!P) 84 return; 85 const TimePointType End = ProgressMeter<ClockType>::ClockType::now(); 86 P->step(End - Begin); 87 } 88 89 ProgressMeterStep(const ProgressMeterStep &) = delete; 90 ProgressMeterStep(ProgressMeterStep &&) = delete; 91 ProgressMeterStep &operator=(const ProgressMeterStep &) = delete; 92 ProgressMeterStep &operator=(ProgressMeterStep &&) = delete; 93 }; 94 95 ProgressMeter(int NumStepsTotal_, raw_ostream &out_ = errs()) Out(out_)96 : Out(out_), NumStepsTotal(NumStepsTotal_) { 97 assert(NumStepsTotal > 0 && "No steps are planned?"); 98 } 99 100 ProgressMeter(const ProgressMeter &) = delete; 101 ProgressMeter(ProgressMeter &&) = delete; 102 ProgressMeter &operator=(const ProgressMeter &) = delete; 103 ProgressMeter &operator=(ProgressMeter &&) = delete; 104 105 private: step(DurationType Elapsed)106 void step(DurationType Elapsed) { 107 assert((ElapsedTotal.getNumSteps() < NumStepsTotal) && "Step overflow!"); 108 assert(Elapsed.count() >= 0 && "Negative time drift detected."); 109 110 auto [OldProgress, OldEta] = eta(); 111 ElapsedTotal.step(Elapsed); 112 auto [NewProgress, NewEta] = eta(); 113 114 if (NewProgress < OldProgress + 1) 115 return; 116 117 Out << format("Processing... %*d%%", 3, NewProgress); 118 if (NewEta) { 119 int SecondsTotal = std::ceil(NewEta->count()); 120 int Seconds = SecondsTotal % 60; 121 int MinutesTotal = SecondsTotal / 60; 122 123 Out << format(", ETA %02d:%02d", MinutesTotal, Seconds); 124 } 125 Out << "\n"; 126 Out.flush(); 127 } 128 eta()129 inline std::pair<CompetionPercentage, std::optional<Sec>> eta() const { 130 CompetionPercentage Progress = 131 (100 * ElapsedTotal.getNumSteps()) / NumStepsTotal; 132 133 std::optional<Sec> ETA; 134 if (std::optional<Sec> AverageStepDuration = 135 ElapsedTotal.template getAverage<Sec>()) 136 ETA = (NumStepsTotal - ElapsedTotal.getNumSteps()) * *AverageStepDuration; 137 138 return {Progress, ETA}; 139 } 140 }; 141 142 } // namespace exegesis 143 } // namespace llvm 144 145 #endif 146