LatencyBenchmarkRunner.cpp 4.8 KB

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