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