LatencyBenchmarkRunner.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. //===-- LatencyBenchmarkRunner.cpp ------------------------------*- 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. #include "LatencyBenchmarkRunner.h"
  9. #include "BenchmarkRunner.h"
  10. #include "Target.h"
  11. #include "llvm/ADT/Twine.h"
  12. #include "llvm/Support/Error.h"
  13. #include <algorithm>
  14. #include <cmath>
  15. namespace llvm {
  16. namespace exegesis {
  17. LatencyBenchmarkRunner::LatencyBenchmarkRunner(
  18. const LLVMState &State, InstructionBenchmark::ModeE Mode,
  19. BenchmarkPhaseSelectorE BenchmarkPhaseSelector,
  20. InstructionBenchmark::ResultAggregationModeE ResultAgg)
  21. : BenchmarkRunner(State, Mode, BenchmarkPhaseSelector) {
  22. assert((Mode == InstructionBenchmark::Latency ||
  23. Mode == InstructionBenchmark::InverseThroughput) &&
  24. "invalid mode");
  25. ResultAggMode = ResultAgg;
  26. }
  27. LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
  28. static double computeVariance(const llvm::SmallVector<int64_t, 4> &Values) {
  29. if (Values.empty())
  30. return 0.0;
  31. double Sum = std::accumulate(Values.begin(), Values.end(), 0.0);
  32. const double Mean = Sum / Values.size();
  33. double Ret = 0;
  34. for (const auto &V : Values) {
  35. double Delta = V - Mean;
  36. Ret += Delta * Delta;
  37. }
  38. return Ret / Values.size();
  39. }
  40. static int64_t findMin(const llvm::SmallVector<int64_t, 4> &Values) {
  41. if (Values.empty())
  42. return 0;
  43. return *std::min_element(Values.begin(), Values.end());
  44. }
  45. static int64_t findMax(const llvm::SmallVector<int64_t, 4> &Values) {
  46. if (Values.empty())
  47. return 0;
  48. return *std::max_element(Values.begin(), Values.end());
  49. }
  50. static int64_t findMean(const llvm::SmallVector<int64_t, 4> &Values) {
  51. if (Values.empty())
  52. return 0;
  53. return std::accumulate(Values.begin(), Values.end(), 0.0) /
  54. static_cast<double>(Values.size());
  55. }
  56. Expected<std::vector<BenchmarkMeasure>> LatencyBenchmarkRunner::runMeasurements(
  57. const FunctionExecutor &Executor) const {
  58. // Cycle measurements include some overhead from the kernel. Repeat the
  59. // measure several times and return the aggregated value, as specified by
  60. // ResultAggMode.
  61. constexpr const int NumMeasurements = 30;
  62. llvm::SmallVector<int64_t, 4> AccumulatedValues;
  63. double MinVariance = std::numeric_limits<double>::infinity();
  64. const char *CounterName = State.getPfmCounters().CycleCounter;
  65. // Values count for each run.
  66. int ValuesCount = 0;
  67. for (size_t I = 0; I < NumMeasurements; ++I) {
  68. auto ExpectedCounterValues = Executor.runAndSample(CounterName);
  69. if (!ExpectedCounterValues)
  70. return ExpectedCounterValues.takeError();
  71. ValuesCount = ExpectedCounterValues.get().size();
  72. if (ValuesCount == 1)
  73. AccumulatedValues.push_back(ExpectedCounterValues.get()[0]);
  74. else {
  75. // We'll keep the reading with lowest variance (ie., most stable)
  76. double Variance = computeVariance(*ExpectedCounterValues);
  77. if (MinVariance > Variance) {
  78. AccumulatedValues = std::move(ExpectedCounterValues.get());
  79. MinVariance = Variance;
  80. }
  81. }
  82. }
  83. std::string ModeName;
  84. switch (Mode) {
  85. case InstructionBenchmark::Latency:
  86. ModeName = "latency";
  87. break;
  88. case InstructionBenchmark::InverseThroughput:
  89. ModeName = "inverse_throughput";
  90. break;
  91. default:
  92. break;
  93. }
  94. switch (ResultAggMode) {
  95. case InstructionBenchmark::MinVariance: {
  96. if (ValuesCount == 1)
  97. llvm::errs() << "Each sample only has one value. result-aggregation-mode "
  98. "of min-variance is probably non-sensical\n";
  99. std::vector<BenchmarkMeasure> Result;
  100. Result.reserve(AccumulatedValues.size());
  101. for (const int64_t Value : AccumulatedValues)
  102. Result.push_back(BenchmarkMeasure::Create(ModeName, Value));
  103. return std::move(Result);
  104. }
  105. case InstructionBenchmark::Min: {
  106. std::vector<BenchmarkMeasure> Result;
  107. Result.push_back(
  108. BenchmarkMeasure::Create(ModeName, findMin(AccumulatedValues)));
  109. return std::move(Result);
  110. }
  111. case InstructionBenchmark::Max: {
  112. std::vector<BenchmarkMeasure> Result;
  113. Result.push_back(
  114. BenchmarkMeasure::Create(ModeName, findMax(AccumulatedValues)));
  115. return std::move(Result);
  116. }
  117. case InstructionBenchmark::Mean: {
  118. std::vector<BenchmarkMeasure> Result;
  119. Result.push_back(
  120. BenchmarkMeasure::Create(ModeName, findMean(AccumulatedValues)));
  121. return std::move(Result);
  122. }
  123. }
  124. return llvm::make_error<Failure>(llvm::Twine("Unexpected benchmark mode(")
  125. .concat(std::to_string(Mode))
  126. .concat(" and unexpected ResultAggMode ")
  127. .concat(std::to_string(ResultAggMode)));
  128. }
  129. } // namespace exegesis
  130. } // namespace llvm