ProgressMeter.h 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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. #ifndef LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H
  9. #define LLVM_TOOLS_LLVM_EXEGESIS_PROGRESSMETER_H
  10. #include "llvm/Support/Format.h"
  11. #include "llvm/Support/raw_ostream.h"
  12. #include <cassert>
  13. #include <chrono>
  14. #include <cmath>
  15. #include <optional>
  16. #include <type_traits>
  17. namespace llvm {
  18. namespace exegesis {
  19. /// Represents `\sum_{i=1..accumulated}{step_i} / accumulated`,
  20. /// where `step_i` is the value passed to the `i`-th call to `step()`,
  21. /// and `accumulated` is the total number of calls to `step()`.
  22. template <typename NumTy, typename DenTy = int> class SimpleMovingAverage {
  23. NumTy Accumulated = NumTy(0);
  24. DenTy Steps = 0;
  25. public:
  26. SimpleMovingAverage() = default;
  27. SimpleMovingAverage(const SimpleMovingAverage &) = delete;
  28. SimpleMovingAverage(SimpleMovingAverage &&) = delete;
  29. SimpleMovingAverage &operator=(const SimpleMovingAverage &) = delete;
  30. SimpleMovingAverage &operator=(SimpleMovingAverage &&) = delete;
  31. inline void step(NumTy Quantity) {
  32. Accumulated += Quantity;
  33. ++Steps;
  34. }
  35. inline NumTy getAccumulated() const { return Accumulated; }
  36. inline DenTy getNumSteps() const { return Steps; }
  37. template <typename AvgTy = NumTy>
  38. inline std::optional<AvgTy> getAverage() const {
  39. if (Steps == 0)
  40. return std::nullopt;
  41. return AvgTy(Accumulated) / Steps;
  42. }
  43. };
  44. template <typename ClockTypeTy = std::chrono::steady_clock,
  45. typename = std::enable_if_t<ClockTypeTy::is_steady>>
  46. class ProgressMeter {
  47. public:
  48. using ClockType = ClockTypeTy;
  49. using TimePointType = std::chrono::time_point<ClockType>;
  50. using DurationType = std::chrono::duration<typename ClockType::rep,
  51. typename ClockType::period>;
  52. using CompetionPercentage = int;
  53. using Sec = std::chrono::duration<double, std::chrono::seconds::period>;
  54. private:
  55. raw_ostream &Out;
  56. const int NumStepsTotal;
  57. SimpleMovingAverage<DurationType> ElapsedTotal;
  58. public:
  59. friend class ProgressMeterStep;
  60. class ProgressMeterStep {
  61. ProgressMeter *P;
  62. const TimePointType Begin;
  63. public:
  64. inline ProgressMeterStep(ProgressMeter *P_)
  65. : P(P_), Begin(P ? ProgressMeter<ClockType>::ClockType::now()
  66. : TimePointType()) {}
  67. inline ~ProgressMeterStep() {
  68. if (!P)
  69. return;
  70. const TimePointType End = ProgressMeter<ClockType>::ClockType::now();
  71. P->step(End - Begin);
  72. }
  73. ProgressMeterStep(const ProgressMeterStep &) = delete;
  74. ProgressMeterStep(ProgressMeterStep &&) = delete;
  75. ProgressMeterStep &operator=(const ProgressMeterStep &) = delete;
  76. ProgressMeterStep &operator=(ProgressMeterStep &&) = delete;
  77. };
  78. ProgressMeter(int NumStepsTotal_, raw_ostream &out_ = llvm::errs())
  79. : Out(out_), NumStepsTotal(NumStepsTotal_) {
  80. assert(NumStepsTotal > 0 && "No steps are planned?");
  81. }
  82. ProgressMeter(const ProgressMeter &) = delete;
  83. ProgressMeter(ProgressMeter &&) = delete;
  84. ProgressMeter &operator=(const ProgressMeter &) = delete;
  85. ProgressMeter &operator=(ProgressMeter &&) = delete;
  86. private:
  87. void step(DurationType Elapsed) {
  88. assert((ElapsedTotal.getNumSteps() < NumStepsTotal) && "Step overflow!");
  89. assert(Elapsed.count() >= 0 && "Negative time drift detected.");
  90. auto [OldProgress, OldEta] = eta();
  91. ElapsedTotal.step(Elapsed);
  92. auto [NewProgress, NewEta] = eta();
  93. if (NewProgress < OldProgress + 1)
  94. return;
  95. Out << format("Processing... %*d%%", 3, NewProgress);
  96. if (NewEta) {
  97. int SecondsTotal = std::ceil(NewEta->count());
  98. int Seconds = SecondsTotal % 60;
  99. int MinutesTotal = SecondsTotal / 60;
  100. Out << format(", ETA %02d:%02d", MinutesTotal, Seconds);
  101. }
  102. Out << "\n";
  103. Out.flush();
  104. }
  105. inline std::pair<CompetionPercentage, std::optional<Sec>> eta() const {
  106. CompetionPercentage Progress =
  107. (100 * ElapsedTotal.getNumSteps()) / NumStepsTotal;
  108. std::optional<Sec> ETA;
  109. if (std::optional<Sec> AverageStepDuration =
  110. ElapsedTotal.template getAverage<Sec>())
  111. ETA = (NumStepsTotal - ElapsedTotal.getNumSteps()) * *AverageStepDuration;
  112. return {Progress, ETA};
  113. }
  114. };
  115. } // namespace exegesis
  116. } // namespace llvm
  117. #endif