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