146 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H10#define LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H11 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 20namespace llvm {21namespace 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()`.26template <typename NumTy, typename DenTy = int> class SimpleMovingAverage {27 NumTy Accumulated = NumTy(0);28 DenTy Steps = 0;29 30public: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 38 inline void step(NumTy Quantity) {39 Accumulated += Quantity;40 ++Steps;41 }42 43 inline NumTy getAccumulated() const { return Accumulated; }44 45 inline DenTy getNumSteps() const { return Steps; }46 47 template <typename AvgTy = NumTy>48 inline std::optional<AvgTy> getAverage() const {49 if (Steps == 0)50 return std::nullopt;51 return AvgTy(Accumulated) / Steps;52 }53};54 55template <typename ClockTypeTy = std::chrono::steady_clock,56 typename = std::enable_if_t<ClockTypeTy::is_steady>>57class ProgressMeter {58public: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 66private:67 raw_ostream &Out;68 const int NumStepsTotal;69 SimpleMovingAverage<DurationType> ElapsedTotal;70 71public:72 friend class ProgressMeterStep;73 class ProgressMeterStep {74 ProgressMeter *P;75 const TimePointType Begin;76 77 public:78 inline ProgressMeterStep(ProgressMeter *P_)79 : P(P_), Begin(P ? ProgressMeter<ClockType>::ClockType::now()80 : TimePointType()) {}81 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())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 105private: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 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 exegesis143} // namespace llvm144 145#endif146