1 //===-- BenchmarkRunner.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 <array>
10 #include <memory>
11 #include <string>
12
13 #include "Assembler.h"
14 #include "BenchmarkRunner.h"
15 #include "Error.h"
16 #include "MCInstrDescView.h"
17 #include "PerfHelper.h"
18 #include "Target.h"
19 #include "llvm/ADT/ScopeExit.h"
20 #include "llvm/ADT/StringExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/ADT/Twine.h"
23 #include "llvm/Support/CrashRecoveryContext.h"
24 #include "llvm/Support/Error.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/MemoryBuffer.h"
27 #include "llvm/Support/Program.h"
28
29 namespace llvm {
30 namespace exegesis {
31
BenchmarkRunner(const LLVMState & State,InstructionBenchmark::ModeE Mode)32 BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
33 InstructionBenchmark::ModeE Mode)
34 : State(State), Mode(Mode), Scratch(std::make_unique<ScratchSpace>()) {}
35
36 BenchmarkRunner::~BenchmarkRunner() = default;
37
38 namespace {
39 class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
40 public:
FunctionExecutorImpl(const LLVMState & State,object::OwningBinary<object::ObjectFile> Obj,BenchmarkRunner::ScratchSpace * Scratch)41 FunctionExecutorImpl(const LLVMState &State,
42 object::OwningBinary<object::ObjectFile> Obj,
43 BenchmarkRunner::ScratchSpace *Scratch)
44 : State(State), Function(State.createTargetMachine(), std::move(Obj)),
45 Scratch(Scratch) {}
46
47 private:
runAndMeasure(const char * Counters) const48 Expected<int64_t> runAndMeasure(const char *Counters) const override {
49 auto ResultOrError = runAndSample(Counters);
50 if (ResultOrError)
51 return ResultOrError.get()[0];
52 return ResultOrError.takeError();
53 }
54
55 static void
accumulateCounterValues(const llvm::SmallVector<int64_t,4> & NewValues,llvm::SmallVector<int64_t,4> * Result)56 accumulateCounterValues(const llvm::SmallVector<int64_t, 4> &NewValues,
57 llvm::SmallVector<int64_t, 4> *Result) {
58
59 const size_t NumValues = std::max(NewValues.size(), Result->size());
60 if (NumValues > Result->size())
61 Result->resize(NumValues, 0);
62 for (size_t I = 0, End = NewValues.size(); I < End; ++I)
63 (*Result)[I] += NewValues[I];
64 }
65
66 Expected<llvm::SmallVector<int64_t, 4>>
runAndSample(const char * Counters) const67 runAndSample(const char *Counters) const override {
68 // We sum counts when there are several counters for a single ProcRes
69 // (e.g. P23 on SandyBridge).
70 llvm::SmallVector<int64_t, 4> CounterValues;
71 int Reserved = 0;
72 SmallVector<StringRef, 2> CounterNames;
73 StringRef(Counters).split(CounterNames, '+');
74 char *const ScratchPtr = Scratch->ptr();
75 for (auto &CounterName : CounterNames) {
76 CounterName = CounterName.trim();
77 auto CounterOrError =
78 State.getExegesisTarget().createCounter(CounterName, State);
79
80 if (!CounterOrError)
81 return CounterOrError.takeError();
82
83 pfm::Counter *Counter = CounterOrError.get().get();
84 if (Reserved == 0) {
85 Reserved = Counter->numValues();
86 CounterValues.reserve(Reserved);
87 } else if (Reserved != Counter->numValues())
88 // It'd be wrong to accumulate vectors of different sizes.
89 return make_error<Failure>(
90 llvm::Twine("Inconsistent number of values for counter ")
91 .concat(CounterName)
92 .concat(std::to_string(Counter->numValues()))
93 .concat(" vs expected of ")
94 .concat(std::to_string(Reserved)));
95 Scratch->clear();
96 {
97 CrashRecoveryContext CRC;
98 CrashRecoveryContext::Enable();
99 const bool Crashed = !CRC.RunSafely([this, Counter, ScratchPtr]() {
100 Counter->start();
101 this->Function(ScratchPtr);
102 Counter->stop();
103 });
104 CrashRecoveryContext::Disable();
105 // FIXME: Better diagnosis.
106 if (Crashed)
107 return make_error<SnippetCrash>("snippet crashed while running");
108 }
109 auto ValueOrError = Counter->readOrError();
110 if (!ValueOrError)
111 return ValueOrError.takeError();
112
113 accumulateCounterValues(ValueOrError.get(), &CounterValues);
114 }
115 return CounterValues;
116 }
117
118 const LLVMState &State;
119 const ExecutableFunction Function;
120 BenchmarkRunner::ScratchSpace *const Scratch;
121 };
122 } // namespace
123
runConfiguration(const BenchmarkCode & BC,unsigned NumRepetitions,ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors,bool DumpObjectToDisk) const124 Expected<InstructionBenchmark> BenchmarkRunner::runConfiguration(
125 const BenchmarkCode &BC, unsigned NumRepetitions,
126 ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors,
127 bool DumpObjectToDisk) const {
128 InstructionBenchmark InstrBenchmark;
129 InstrBenchmark.Mode = Mode;
130 InstrBenchmark.CpuName = std::string(State.getTargetMachine().getTargetCPU());
131 InstrBenchmark.LLVMTriple =
132 State.getTargetMachine().getTargetTriple().normalize();
133 InstrBenchmark.NumRepetitions = NumRepetitions;
134 InstrBenchmark.Info = BC.Info;
135
136 const std::vector<MCInst> &Instructions = BC.Key.Instructions;
137
138 InstrBenchmark.Key = BC.Key;
139
140 // If we end up having an error, and we've previously succeeded with
141 // some other Repetitor, we want to discard the previous measurements.
142 struct ClearBenchmarkOnReturn {
143 ClearBenchmarkOnReturn(InstructionBenchmark *IB) : IB(IB) {}
144 ~ClearBenchmarkOnReturn() {
145 if (Clear)
146 IB->Measurements.clear();
147 }
148 void disarm() { Clear = false; }
149
150 private:
151 InstructionBenchmark *const IB;
152 bool Clear = true;
153 };
154 ClearBenchmarkOnReturn CBOR(&InstrBenchmark);
155
156 for (const std::unique_ptr<const SnippetRepetitor> &Repetitor : Repetitors) {
157 // Assemble at least kMinInstructionsForSnippet instructions by repeating
158 // the snippet for debug/analysis. This is so that the user clearly
159 // understands that the inside instructions are repeated.
160 constexpr const int kMinInstructionsForSnippet = 16;
161 {
162 SmallString<0> Buffer;
163 raw_svector_ostream OS(Buffer);
164 if (Error E = assembleToStream(
165 State.getExegesisTarget(), State.createTargetMachine(),
166 BC.LiveIns, BC.Key.RegisterInitialValues,
167 Repetitor->Repeat(Instructions, kMinInstructionsForSnippet),
168 OS)) {
169 return std::move(E);
170 }
171 const ExecutableFunction EF(State.createTargetMachine(),
172 getObjectFromBuffer(OS.str()));
173 const auto FnBytes = EF.getFunctionBytes();
174 InstrBenchmark.AssembledSnippet.insert(
175 InstrBenchmark.AssembledSnippet.end(), FnBytes.begin(),
176 FnBytes.end());
177 }
178
179 // Assemble NumRepetitions instructions repetitions of the snippet for
180 // measurements.
181 const auto Filler =
182 Repetitor->Repeat(Instructions, InstrBenchmark.NumRepetitions);
183
184 object::OwningBinary<object::ObjectFile> ObjectFile;
185 if (DumpObjectToDisk) {
186 auto ObjectFilePath = writeObjectFile(BC, Filler);
187 if (Error E = ObjectFilePath.takeError()) {
188 InstrBenchmark.Error = toString(std::move(E));
189 return InstrBenchmark;
190 }
191 outs() << "Check generated assembly with: /usr/bin/objdump -d "
192 << *ObjectFilePath << "\n";
193 ObjectFile = getObjectFromFile(*ObjectFilePath);
194 } else {
195 SmallString<0> Buffer;
196 raw_svector_ostream OS(Buffer);
197 if (Error E = assembleToStream(
198 State.getExegesisTarget(), State.createTargetMachine(),
199 BC.LiveIns, BC.Key.RegisterInitialValues, Filler, OS)) {
200 return std::move(E);
201 }
202 ObjectFile = getObjectFromBuffer(OS.str());
203 }
204
205 const FunctionExecutorImpl Executor(State, std::move(ObjectFile),
206 Scratch.get());
207 auto NewMeasurements = runMeasurements(Executor);
208 if (Error E = NewMeasurements.takeError()) {
209 if (!E.isA<SnippetCrash>())
210 return std::move(E);
211 InstrBenchmark.Error = toString(std::move(E));
212 return InstrBenchmark;
213 }
214 assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
215 for (BenchmarkMeasure &BM : *NewMeasurements) {
216 // Scale the measurements by instruction.
217 BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
218 // Scale the measurements by snippet.
219 BM.PerSnippetValue *= static_cast<double>(Instructions.size()) /
220 InstrBenchmark.NumRepetitions;
221 }
222 if (InstrBenchmark.Measurements.empty()) {
223 InstrBenchmark.Measurements = std::move(*NewMeasurements);
224 continue;
225 }
226
227 assert(Repetitors.size() > 1 && !InstrBenchmark.Measurements.empty() &&
228 "We're in an 'min' repetition mode, and need to aggregate new "
229 "result to the existing result.");
230 assert(InstrBenchmark.Measurements.size() == NewMeasurements->size() &&
231 "Expected to have identical number of measurements.");
232 for (auto I : zip(InstrBenchmark.Measurements, *NewMeasurements)) {
233 BenchmarkMeasure &Measurement = std::get<0>(I);
234 BenchmarkMeasure &NewMeasurement = std::get<1>(I);
235 assert(Measurement.Key == NewMeasurement.Key &&
236 "Expected measurements to be symmetric");
237
238 Measurement.PerInstructionValue = std::min(
239 Measurement.PerInstructionValue, NewMeasurement.PerInstructionValue);
240 Measurement.PerSnippetValue =
241 std::min(Measurement.PerSnippetValue, NewMeasurement.PerSnippetValue);
242 }
243 }
244
245 // We successfully measured everything, so don't discard the results.
246 CBOR.disarm();
247 return InstrBenchmark;
248 }
249
250 Expected<std::string>
writeObjectFile(const BenchmarkCode & BC,const FillFunction & FillFunction) const251 BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
252 const FillFunction &FillFunction) const {
253 int ResultFD = 0;
254 SmallString<256> ResultPath;
255 if (Error E = errorCodeToError(
256 sys::fs::createTemporaryFile("snippet", "o", ResultFD, ResultPath)))
257 return std::move(E);
258 raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
259 if (Error E = assembleToStream(
260 State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns,
261 BC.Key.RegisterInitialValues, FillFunction, OFS)) {
262 return std::move(E);
263 }
264 return std::string(ResultPath.str());
265 }
266
~FunctionExecutor()267 BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
268
269 } // namespace exegesis
270 } // namespace llvm
271