1 //===-- LatencyBenchmarkRunner.cpp ------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "LatencyBenchmarkRunner.h"
11 #include "BenchmarkRunner.h"
13 #include "llvm/ADT/Twine.h"
14 #include "llvm/Support/Error.h"
18 #define DEBUG_TYPE "exegesis-latency-benchmarkrunner"
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
,
30 assert((Mode
== Benchmark::Latency
|| Mode
== Benchmark::InverseThroughput
) &&
32 ResultAggMode
= ResultAgg
;
33 NumMeasurements
= BenchmarkRepeatCount
;
36 LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
38 static double computeVariance(const SmallVector
<int64_t, 4> &Values
) {
41 double Sum
= std::accumulate(Values
.begin(), Values
.end(), 0.0);
43 const double Mean
= Sum
/ Values
.size();
45 for (const auto &V
: Values
) {
46 double Delta
= V
- Mean
;
49 return Ret
/ Values
.size();
52 static int64_t findMin(const SmallVector
<int64_t, 4> &Values
) {
55 return *llvm::min_element(Values
);
58 static int64_t findMax(const SmallVector
<int64_t, 4> &Values
) {
61 return *llvm::max_element(Values
);
64 static int64_t findMean(const SmallVector
<int64_t, 4> &Values
) {
67 return std::accumulate(Values
.begin(), Values
.end(), 0.0) /
68 static_cast<double>(Values
.size());
71 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
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
;
81 SmallVector
<const char *> ValCountersToRun
;
82 Error ValCounterErr
= getValidationCountersToRun(ValCountersToRun
);
84 return std::move(ValCounterErr
);
86 SmallVector
<int64_t> ValCounterValues(ValCountersToRun
.size(), 0);
87 // Values count for each run.
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]
99 AccumulatedValues
.push_back(ExpectedCounterValues
.get()[0]);
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
;
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
];
116 std::map
<ValidationEvent
, int64_t> ValidationInfo
;
117 for (size_t I
= 0; I
< ValidationCounters
.size(); ++I
)
118 ValidationInfo
[ValidationCounters
[I
]] = ValCounterValues
[I
];
120 std::string ModeName
;
122 case Benchmark::Latency
:
123 ModeName
= "latency";
125 case Benchmark::InverseThroughput
:
126 ModeName
= "inverse_throughput";
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
)
141 BenchmarkMeasure::Create(ModeName
, Value
, ValidationInfo
));
142 return std::move(Result
);
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
);
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
);
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
);
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
)));
169 } // namespace exegesis