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 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: 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: 48 Expected<int64_t> runAndMeasure(const char *Counters) const override { 49 // We sum counts when there are several counters for a single ProcRes 50 // (e.g. P23 on SandyBridge). 51 int64_t CounterValue = 0; 52 SmallVector<StringRef, 2> CounterNames; 53 StringRef(Counters).split(CounterNames, '+'); 54 char *const ScratchPtr = Scratch->ptr(); 55 for (auto &CounterName : CounterNames) { 56 CounterName = CounterName.trim(); 57 auto CounterOrError = 58 State.getExegesisTarget().createCounter(CounterName, State); 59 60 if (!CounterOrError) 61 return CounterOrError.takeError(); 62 63 pfm::Counter *Counter = CounterOrError.get().get(); 64 Scratch->clear(); 65 { 66 CrashRecoveryContext CRC; 67 CrashRecoveryContext::Enable(); 68 const bool Crashed = !CRC.RunSafely([this, Counter, ScratchPtr]() { 69 Counter->start(); 70 this->Function(ScratchPtr); 71 Counter->stop(); 72 }); 73 CrashRecoveryContext::Disable(); 74 // FIXME: Better diagnosis. 75 if (Crashed) 76 return make_error<SnippetCrash>("snippet crashed while running"); 77 } 78 CounterValue += Counter->read(); 79 } 80 return CounterValue; 81 } 82 83 const LLVMState &State; 84 const ExecutableFunction Function; 85 BenchmarkRunner::ScratchSpace *const Scratch; 86 }; 87 } // namespace 88 89 Expected<InstructionBenchmark> BenchmarkRunner::runConfiguration( 90 const BenchmarkCode &BC, unsigned NumRepetitions, 91 ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors, 92 bool DumpObjectToDisk) const { 93 InstructionBenchmark InstrBenchmark; 94 InstrBenchmark.Mode = Mode; 95 InstrBenchmark.CpuName = std::string(State.getTargetMachine().getTargetCPU()); 96 InstrBenchmark.LLVMTriple = 97 State.getTargetMachine().getTargetTriple().normalize(); 98 InstrBenchmark.NumRepetitions = NumRepetitions; 99 InstrBenchmark.Info = BC.Info; 100 101 const std::vector<MCInst> &Instructions = BC.Key.Instructions; 102 103 InstrBenchmark.Key = BC.Key; 104 105 // If we end up having an error, and we've previously succeeded with 106 // some other Repetitor, we want to discard the previous measurements. 107 struct ClearBenchmarkOnReturn { 108 ClearBenchmarkOnReturn(InstructionBenchmark *IB) : IB(IB) {} 109 ~ClearBenchmarkOnReturn() { 110 if (Clear) 111 IB->Measurements.clear(); 112 } 113 void disarm() { Clear = false; } 114 115 private: 116 InstructionBenchmark *const IB; 117 bool Clear = true; 118 }; 119 ClearBenchmarkOnReturn CBOR(&InstrBenchmark); 120 121 for (const std::unique_ptr<const SnippetRepetitor> &Repetitor : Repetitors) { 122 // Assemble at least kMinInstructionsForSnippet instructions by repeating 123 // the snippet for debug/analysis. This is so that the user clearly 124 // understands that the inside instructions are repeated. 125 constexpr const int kMinInstructionsForSnippet = 16; 126 { 127 SmallString<0> Buffer; 128 raw_svector_ostream OS(Buffer); 129 if (Error E = assembleToStream( 130 State.getExegesisTarget(), State.createTargetMachine(), 131 BC.LiveIns, BC.Key.RegisterInitialValues, 132 Repetitor->Repeat(Instructions, kMinInstructionsForSnippet), 133 OS)) { 134 return std::move(E); 135 } 136 const ExecutableFunction EF(State.createTargetMachine(), 137 getObjectFromBuffer(OS.str())); 138 const auto FnBytes = EF.getFunctionBytes(); 139 InstrBenchmark.AssembledSnippet.insert( 140 InstrBenchmark.AssembledSnippet.end(), FnBytes.begin(), 141 FnBytes.end()); 142 } 143 144 // Assemble NumRepetitions instructions repetitions of the snippet for 145 // measurements. 146 const auto Filler = 147 Repetitor->Repeat(Instructions, InstrBenchmark.NumRepetitions); 148 149 object::OwningBinary<object::ObjectFile> ObjectFile; 150 if (DumpObjectToDisk) { 151 auto ObjectFilePath = writeObjectFile(BC, Filler); 152 if (Error E = ObjectFilePath.takeError()) { 153 InstrBenchmark.Error = toString(std::move(E)); 154 return InstrBenchmark; 155 } 156 outs() << "Check generated assembly with: /usr/bin/objdump -d " 157 << *ObjectFilePath << "\n"; 158 ObjectFile = getObjectFromFile(*ObjectFilePath); 159 } else { 160 SmallString<0> Buffer; 161 raw_svector_ostream OS(Buffer); 162 if (Error E = assembleToStream( 163 State.getExegesisTarget(), State.createTargetMachine(), 164 BC.LiveIns, BC.Key.RegisterInitialValues, Filler, OS)) { 165 return std::move(E); 166 } 167 ObjectFile = getObjectFromBuffer(OS.str()); 168 } 169 170 const FunctionExecutorImpl Executor(State, std::move(ObjectFile), 171 Scratch.get()); 172 auto NewMeasurements = runMeasurements(Executor); 173 if (Error E = NewMeasurements.takeError()) { 174 if (!E.isA<SnippetCrash>()) 175 return std::move(E); 176 InstrBenchmark.Error = toString(std::move(E)); 177 return InstrBenchmark; 178 } 179 assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions"); 180 for (BenchmarkMeasure &BM : *NewMeasurements) { 181 // Scale the measurements by instruction. 182 BM.PerInstructionValue /= InstrBenchmark.NumRepetitions; 183 // Scale the measurements by snippet. 184 BM.PerSnippetValue *= static_cast<double>(Instructions.size()) / 185 InstrBenchmark.NumRepetitions; 186 } 187 if (InstrBenchmark.Measurements.empty()) { 188 InstrBenchmark.Measurements = std::move(*NewMeasurements); 189 continue; 190 } 191 192 assert(Repetitors.size() > 1 && !InstrBenchmark.Measurements.empty() && 193 "We're in an 'min' repetition mode, and need to aggregate new " 194 "result to the existing result."); 195 assert(InstrBenchmark.Measurements.size() == NewMeasurements->size() && 196 "Expected to have identical number of measurements."); 197 for (auto I : zip(InstrBenchmark.Measurements, *NewMeasurements)) { 198 BenchmarkMeasure &Measurement = std::get<0>(I); 199 BenchmarkMeasure &NewMeasurement = std::get<1>(I); 200 assert(Measurement.Key == NewMeasurement.Key && 201 "Expected measurements to be symmetric"); 202 203 Measurement.PerInstructionValue = std::min( 204 Measurement.PerInstructionValue, NewMeasurement.PerInstructionValue); 205 Measurement.PerSnippetValue = 206 std::min(Measurement.PerSnippetValue, NewMeasurement.PerSnippetValue); 207 } 208 } 209 210 // We successfully measured everything, so don't discard the results. 211 CBOR.disarm(); 212 return InstrBenchmark; 213 } 214 215 Expected<std::string> 216 BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC, 217 const FillFunction &FillFunction) const { 218 int ResultFD = 0; 219 SmallString<256> ResultPath; 220 if (Error E = errorCodeToError( 221 sys::fs::createTemporaryFile("snippet", "o", ResultFD, ResultPath))) 222 return std::move(E); 223 raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); 224 if (Error E = assembleToStream( 225 State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns, 226 BC.Key.RegisterInitialValues, FillFunction, OFS)) { 227 return std::move(E); 228 } 229 return std::string(ResultPath.str()); 230 } 231 232 BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {} 233 234 } // namespace exegesis 235 } // namespace llvm 236