BenchmarkRunner.cpp 10 KB

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