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