blob: 6cdefb8b067918d9c2ef0d9274fce0f76a878f29 [file] [log] [blame]
Miloš Stojanović24b7b992020-01-17 14:28:54 +01001//===-- 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
Miloš Stojanović24b7b992020-01-17 14:28:54 +010011#include "BenchmarkRunner.h"
Vy Nguyene086a392020-06-25 11:15:16 -040012#include "Target.h"
13#include "llvm/ADT/Twine.h"
14#include "llvm/Support/Error.h"
15#include <algorithm>
16#include <cmath>
Miloš Stojanović24b7b992020-01-17 14:28:54 +010017
18namespace llvm {
19namespace exegesis {
20
Vy Nguyene086a392020-06-25 11:15:16 -040021LatencyBenchmarkRunner::LatencyBenchmarkRunner(
22 const LLVMState &State, InstructionBenchmark::ModeE Mode,
23 InstructionBenchmark::ResultAggregationModeE ResultAgg)
Miloš Stojanović24b7b992020-01-17 14:28:54 +010024 : BenchmarkRunner(State, Mode) {
25 assert((Mode == InstructionBenchmark::Latency ||
26 Mode == InstructionBenchmark::InverseThroughput) &&
27 "invalid mode");
Vy Nguyene086a392020-06-25 11:15:16 -040028 ResultAggMode = ResultAgg;
Miloš Stojanović24b7b992020-01-17 14:28:54 +010029}
30
31LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
32
Vy Nguyene086a392020-06-25 11:15:16 -040033static 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
47static 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
53static 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
59static 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
Miloš Stojanović24b7b992020-01-17 14:28:54 +010066Expected<std::vector<BenchmarkMeasure>> LatencyBenchmarkRunner::runMeasurements(
67 const FunctionExecutor &Executor) const {
68 // Cycle measurements include some overhead from the kernel. Repeat the
Vy Nguyene086a392020-06-25 11:15:16 -040069 // measure several times and return the aggregated value, as specified by
70 // ResultAggMode.
Miloš Stojanović24b7b992020-01-17 14:28:54 +010071 constexpr const int NumMeasurements = 30;
Vy Nguyene086a392020-06-25 11:15:16 -040072 llvm::SmallVector<int64_t, 4> AccumulatedValues;
73 double MinVariance = std::numeric_limits<double>::infinity();
Miloš Stojanović24b7b992020-01-17 14:28:54 +010074 const char *CounterName = State.getPfmCounters().CycleCounter;
Vy Nguyene086a392020-06-25 11:15:16 -040075 // Values count for each run.
76 int ValuesCount = 0;
Miloš Stojanović24b7b992020-01-17 14:28:54 +010077 for (size_t I = 0; I < NumMeasurements; ++I) {
Vy Nguyene086a392020-06-25 11:15:16 -040078 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 }
Miloš Stojanović24b7b992020-01-17 14:28:54 +010092 }
Vy Nguyene086a392020-06-25 11:15:16 -040093
94 std::string ModeName;
Miloš Stojanović24b7b992020-01-17 14:28:54 +010095 switch (Mode) {
96 case InstructionBenchmark::Latency:
Vy Nguyene086a392020-06-25 11:15:16 -040097 ModeName = "latency";
Miloš Stojanović24b7b992020-01-17 14:28:54 +010098 break;
99 case InstructionBenchmark::InverseThroughput:
Vy Nguyene086a392020-06-25 11:15:16 -0400100 ModeName = "inverse_throughput";
Miloš Stojanović24b7b992020-01-17 14:28:54 +0100101 break;
102 default:
103 break;
104 }
Vy Nguyene086a392020-06-25 11:15:16 -0400105
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)));
Miloš Stojanović24b7b992020-01-17 14:28:54 +0100140}
141
142} // namespace exegesis
143} // namespace llvm