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
20namespace llvm {
21namespace exegesis {
22
23LatencyBenchmarkRunner::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
36LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
37
38static double computeVariance(const SmallVector<int64_t, 4> &Values) {
39 if (Values.empty())
40 return 0.0;
41 double Sum = std::accumulate(first: Values.begin(), last: Values.end(), init: 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
52static int64_t findMin(const SmallVector<int64_t, 4> &Values) {
53 if (Values.empty())
54 return 0;
55 return *llvm::min_element(Range: Values);
56}
57
58static int64_t findMax(const SmallVector<int64_t, 4> &Values) {
59 if (Values.empty())
60 return 0;
61 return *llvm::max_element(Range: Values);
62}
63
64static int64_t findMean(const SmallVector<int64_t, 4> &Values) {
65 if (Values.empty())
66 return 0;
67 return std::accumulate(first: Values.begin(), last: Values.end(), init: 0.0) /
68 static_cast<double>(Values.size());
69}
70
71Expected<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 Counters: CounterName, ValidationCounters: ValCountersToRun, ValidationCounterValues&: 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(Elt: ExpectedCounterValues.get()[0]);
100 } else {
101 // We'll keep the reading with lowest variance (ie., most stable)
102 double Variance = computeVariance(Values: *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(n: AccumulatedValues.size());
139 for (const int64_t Value : AccumulatedValues)
140 Result.push_back(
141 x: BenchmarkMeasure::Create(Key: ModeName, Value, ValCounters: ValidationInfo));
142 return std::move(Result);
143 }
144 case Benchmark::Min: {
145 std::vector<BenchmarkMeasure> Result;
146 Result.push_back(x: BenchmarkMeasure::Create(
147 Key: ModeName, Value: findMin(Values: AccumulatedValues), ValCounters: ValidationInfo));
148 return std::move(Result);
149 }
150 case Benchmark::Max: {
151 std::vector<BenchmarkMeasure> Result;
152 Result.push_back(x: BenchmarkMeasure::Create(
153 Key: ModeName, Value: findMax(Values: AccumulatedValues), ValCounters: ValidationInfo));
154 return std::move(Result);
155 }
156 case Benchmark::Mean: {
157 std::vector<BenchmarkMeasure> Result;
158 Result.push_back(x: BenchmarkMeasure::Create(
159 Key: ModeName, Value: findMean(Values: AccumulatedValues), ValCounters: ValidationInfo));
160 return std::move(Result);
161 }
162 }
163 return make_error<Failure>(Args: Twine("Unexpected benchmark mode(")
164 .concat(Suffix: std::to_string(val: Mode))
165 .concat(Suffix: " and unexpected ResultAggMode ")
166 .concat(Suffix: std::to_string(val: ResultAggMode)));
167}
168
169} // namespace exegesis
170} // namespace llvm
171