xref: /llvm-project/llvm/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.cpp (revision fab2bb8bfda865bd438dee981d7be7df8017b76d)
124b7b99bSMiloš Stojanović //===-- LatencyBenchmarkRunner.cpp ------------------------------*- C++ -*-===//
224b7b99bSMiloš Stojanović //
324b7b99bSMiloš Stojanović // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
424b7b99bSMiloš Stojanović // See https://llvm.org/LICENSE.txt for license information.
524b7b99bSMiloš Stojanović // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
624b7b99bSMiloš Stojanović //
724b7b99bSMiloš Stojanović //===----------------------------------------------------------------------===//
824b7b99bSMiloš Stojanović 
924b7b99bSMiloš Stojanović #include "LatencyBenchmarkRunner.h"
1024b7b99bSMiloš Stojanović 
1124b7b99bSMiloš Stojanović #include "BenchmarkRunner.h"
12e086a39cSVy Nguyen #include "Target.h"
13e086a39cSVy Nguyen #include "llvm/ADT/Twine.h"
14e086a39cSVy Nguyen #include "llvm/Support/Error.h"
15e086a39cSVy Nguyen #include <algorithm>
16e086a39cSVy Nguyen #include <cmath>
1724b7b99bSMiloš Stojanović 
1893db5b7bSAiden Grossman #define DEBUG_TYPE "exegesis-latency-benchmarkrunner"
1993db5b7bSAiden Grossman 
2024b7b99bSMiloš Stojanović namespace llvm {
2124b7b99bSMiloš Stojanović namespace exegesis {
2224b7b99bSMiloš Stojanović 
LatencyBenchmarkRunner(const LLVMState & State,Benchmark::ModeE Mode,BenchmarkPhaseSelectorE BenchmarkPhaseSelector,Benchmark::ResultAggregationModeE ResultAgg,ExecutionModeE ExecutionMode,ArrayRef<ValidationEvent> ValCounters,unsigned BenchmarkRepeatCount)23e086a39cSVy Nguyen LatencyBenchmarkRunner::LatencyBenchmarkRunner(
24389bf5d8SAiden Grossman     const LLVMState &State, Benchmark::ModeE Mode,
25e0ad2af6SRoman Lebedev     BenchmarkPhaseSelectorE BenchmarkPhaseSelector,
26fc791b61SAiden Grossman     Benchmark::ResultAggregationModeE ResultAgg, ExecutionModeE ExecutionMode,
27f670112aSAiden Grossman     ArrayRef<ValidationEvent> ValCounters, unsigned BenchmarkRepeatCount)
28f670112aSAiden Grossman     : BenchmarkRunner(State, Mode, BenchmarkPhaseSelector, ExecutionMode,
29f670112aSAiden Grossman                       ValCounters) {
3072df12ccSAiden Grossman   assert((Mode == Benchmark::Latency || Mode == Benchmark::InverseThroughput) &&
3124b7b99bSMiloš Stojanović          "invalid mode");
32e086a39cSVy Nguyen   ResultAggMode = ResultAgg;
33fc791b61SAiden Grossman   NumMeasurements = BenchmarkRepeatCount;
3424b7b99bSMiloš Stojanović }
3524b7b99bSMiloš Stojanović 
3624b7b99bSMiloš Stojanović LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
3724b7b99bSMiloš Stojanović 
computeVariance(const SmallVector<int64_t,4> & Values)38faf675ceSAiden Grossman static double computeVariance(const SmallVector<int64_t, 4> &Values) {
39e086a39cSVy Nguyen   if (Values.empty())
40e086a39cSVy Nguyen     return 0.0;
41e086a39cSVy Nguyen   double Sum = std::accumulate(Values.begin(), Values.end(), 0.0);
42e086a39cSVy Nguyen 
43e086a39cSVy Nguyen   const double Mean = Sum / Values.size();
44e086a39cSVy Nguyen   double Ret = 0;
45e086a39cSVy Nguyen   for (const auto &V : Values) {
46e086a39cSVy Nguyen     double Delta = V - Mean;
47e086a39cSVy Nguyen     Ret += Delta * Delta;
48e086a39cSVy Nguyen   }
49e086a39cSVy Nguyen   return Ret / Values.size();
50e086a39cSVy Nguyen }
51e086a39cSVy Nguyen 
findMin(const SmallVector<int64_t,4> & Values)52faf675ceSAiden Grossman static int64_t findMin(const SmallVector<int64_t, 4> &Values) {
53e086a39cSVy Nguyen   if (Values.empty())
54e086a39cSVy Nguyen     return 0;
55*fab2bb8bSJustin Lebar   return *llvm::min_element(Values);
56e086a39cSVy Nguyen }
57e086a39cSVy Nguyen 
findMax(const SmallVector<int64_t,4> & Values)58faf675ceSAiden Grossman static int64_t findMax(const SmallVector<int64_t, 4> &Values) {
59e086a39cSVy Nguyen   if (Values.empty())
60e086a39cSVy Nguyen     return 0;
61*fab2bb8bSJustin Lebar   return *llvm::max_element(Values);
62e086a39cSVy Nguyen }
63e086a39cSVy Nguyen 
findMean(const SmallVector<int64_t,4> & Values)64faf675ceSAiden Grossman static int64_t findMean(const SmallVector<int64_t, 4> &Values) {
65e086a39cSVy Nguyen   if (Values.empty())
66e086a39cSVy Nguyen     return 0;
67e086a39cSVy Nguyen   return std::accumulate(Values.begin(), Values.end(), 0.0) /
68e086a39cSVy Nguyen          static_cast<double>(Values.size());
69e086a39cSVy Nguyen }
70e086a39cSVy Nguyen 
runMeasurements(const FunctionExecutor & Executor) const7124b7b99bSMiloš Stojanović Expected<std::vector<BenchmarkMeasure>> LatencyBenchmarkRunner::runMeasurements(
7224b7b99bSMiloš Stojanović     const FunctionExecutor &Executor) const {
7324b7b99bSMiloš Stojanović   // Cycle measurements include some overhead from the kernel. Repeat the
74e086a39cSVy Nguyen   // measure several times and return the aggregated value, as specified by
75e086a39cSVy Nguyen   // ResultAggMode.
76faf675ceSAiden Grossman   SmallVector<int64_t, 4> AccumulatedValues;
77e086a39cSVy Nguyen   double MinVariance = std::numeric_limits<double>::infinity();
78f670112aSAiden Grossman   const PfmCountersInfo &PCI = State.getPfmCounters();
79f670112aSAiden Grossman   const char *CounterName = PCI.CycleCounter;
80f670112aSAiden Grossman 
81f670112aSAiden Grossman   SmallVector<const char *> ValCountersToRun;
82f670112aSAiden Grossman   Error ValCounterErr = getValidationCountersToRun(ValCountersToRun);
83f670112aSAiden Grossman   if (ValCounterErr)
84f670112aSAiden Grossman     return std::move(ValCounterErr);
85f670112aSAiden Grossman 
86f670112aSAiden Grossman   SmallVector<int64_t> ValCounterValues(ValCountersToRun.size(), 0);
87e086a39cSVy Nguyen   // Values count for each run.
88e086a39cSVy Nguyen   int ValuesCount = 0;
8924b7b99bSMiloš Stojanović   for (size_t I = 0; I < NumMeasurements; ++I) {
90f670112aSAiden Grossman     SmallVector<int64_t> IterationValCounterValues(ValCountersToRun.size(), -1);
91f670112aSAiden Grossman     auto ExpectedCounterValues = Executor.runAndSample(
92f670112aSAiden Grossman         CounterName, ValCountersToRun, IterationValCounterValues);
93e086a39cSVy Nguyen     if (!ExpectedCounterValues)
94e086a39cSVy Nguyen       return ExpectedCounterValues.takeError();
95e086a39cSVy Nguyen     ValuesCount = ExpectedCounterValues.get().size();
9693db5b7bSAiden Grossman     if (ValuesCount == 1) {
9793db5b7bSAiden Grossman       LLVM_DEBUG(dbgs() << "Latency value: " << ExpectedCounterValues.get()[0]
9893db5b7bSAiden Grossman                         << "\n");
99e086a39cSVy Nguyen       AccumulatedValues.push_back(ExpectedCounterValues.get()[0]);
10093db5b7bSAiden Grossman     } else {
101e086a39cSVy Nguyen       // We'll keep the reading with lowest variance (ie., most stable)
102e086a39cSVy Nguyen       double Variance = computeVariance(*ExpectedCounterValues);
103e086a39cSVy Nguyen       if (MinVariance > Variance) {
104e086a39cSVy Nguyen         AccumulatedValues = std::move(ExpectedCounterValues.get());
105e086a39cSVy Nguyen         MinVariance = Variance;
10624b7b99bSMiloš Stojanović       }
107e086a39cSVy Nguyen     }
108f670112aSAiden Grossman 
10993db5b7bSAiden Grossman     for (size_t I = 0; I < ValCounterValues.size(); ++I) {
1108b84de26SClement Courbet       LLVM_DEBUG(dbgs() << getValidationEventName(ValidationCounters[I]) << ": "
1118b84de26SClement Courbet                         << IterationValCounterValues[I] << "\n");
112f670112aSAiden Grossman       ValCounterValues[I] += IterationValCounterValues[I];
113e086a39cSVy Nguyen     }
11493db5b7bSAiden Grossman   }
115e086a39cSVy Nguyen 
116f670112aSAiden Grossman   std::map<ValidationEvent, int64_t> ValidationInfo;
117f670112aSAiden Grossman   for (size_t I = 0; I < ValidationCounters.size(); ++I)
118f670112aSAiden Grossman     ValidationInfo[ValidationCounters[I]] = ValCounterValues[I];
119f670112aSAiden Grossman 
120e086a39cSVy Nguyen   std::string ModeName;
12124b7b99bSMiloš Stojanović   switch (Mode) {
122389bf5d8SAiden Grossman   case Benchmark::Latency:
123e086a39cSVy Nguyen     ModeName = "latency";
12424b7b99bSMiloš Stojanović     break;
125389bf5d8SAiden Grossman   case Benchmark::InverseThroughput:
126e086a39cSVy Nguyen     ModeName = "inverse_throughput";
12724b7b99bSMiloš Stojanović     break;
12824b7b99bSMiloš Stojanović   default:
12924b7b99bSMiloš Stojanović     break;
13024b7b99bSMiloš Stojanović   }
131e086a39cSVy Nguyen 
132e086a39cSVy Nguyen   switch (ResultAggMode) {
133389bf5d8SAiden Grossman   case Benchmark::MinVariance: {
134e086a39cSVy Nguyen     if (ValuesCount == 1)
135faf675ceSAiden Grossman       errs() << "Each sample only has one value. result-aggregation-mode "
136e086a39cSVy Nguyen                 "of min-variance is probably non-sensical\n";
137e086a39cSVy Nguyen     std::vector<BenchmarkMeasure> Result;
138e086a39cSVy Nguyen     Result.reserve(AccumulatedValues.size());
139e086a39cSVy Nguyen     for (const int64_t Value : AccumulatedValues)
140f670112aSAiden Grossman       Result.push_back(
141f670112aSAiden Grossman           BenchmarkMeasure::Create(ModeName, Value, ValidationInfo));
142c55cf4afSBill Wendling     return std::move(Result);
14324b7b99bSMiloš Stojanović   }
144389bf5d8SAiden Grossman   case Benchmark::Min: {
145e086a39cSVy Nguyen     std::vector<BenchmarkMeasure> Result;
146f670112aSAiden Grossman     Result.push_back(BenchmarkMeasure::Create(
147f670112aSAiden Grossman         ModeName, findMin(AccumulatedValues), ValidationInfo));
148e086a39cSVy Nguyen     return std::move(Result);
149e086a39cSVy Nguyen   }
150389bf5d8SAiden Grossman   case Benchmark::Max: {
151e086a39cSVy Nguyen     std::vector<BenchmarkMeasure> Result;
152f670112aSAiden Grossman     Result.push_back(BenchmarkMeasure::Create(
153f670112aSAiden Grossman         ModeName, findMax(AccumulatedValues), ValidationInfo));
154e086a39cSVy Nguyen     return std::move(Result);
155e086a39cSVy Nguyen   }
156389bf5d8SAiden Grossman   case Benchmark::Mean: {
157e086a39cSVy Nguyen     std::vector<BenchmarkMeasure> Result;
158f670112aSAiden Grossman     Result.push_back(BenchmarkMeasure::Create(
159f670112aSAiden Grossman         ModeName, findMean(AccumulatedValues), ValidationInfo));
160e086a39cSVy Nguyen     return std::move(Result);
161e086a39cSVy Nguyen   }
162e086a39cSVy Nguyen   }
163faf675ceSAiden Grossman   return make_error<Failure>(Twine("Unexpected benchmark mode(")
164e086a39cSVy Nguyen                                  .concat(std::to_string(Mode))
165e086a39cSVy Nguyen                                  .concat(" and unexpected ResultAggMode ")
166e086a39cSVy Nguyen                                  .concat(std::to_string(ResultAggMode)));
167e086a39cSVy Nguyen }
16824b7b99bSMiloš Stojanović 
16924b7b99bSMiloš Stojanović } // namespace exegesis
17024b7b99bSMiloš Stojanović } // namespace llvm
171