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