BenchmarkRunner.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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. BenchmarkPhaseSelectorE BenchmarkPhaseSelector)
  31. : State(State), Mode(Mode), BenchmarkPhaseSelector(BenchmarkPhaseSelector),
  32. Scratch(std::make_unique<ScratchSpace>()) {}
  33. BenchmarkRunner::~BenchmarkRunner() = default;
  34. namespace {
  35. class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
  36. public:
  37. FunctionExecutorImpl(const LLVMState &State,
  38. object::OwningBinary<object::ObjectFile> Obj,
  39. BenchmarkRunner::ScratchSpace *Scratch)
  40. : State(State), Function(State.createTargetMachine(), std::move(Obj)),
  41. Scratch(Scratch) {}
  42. private:
  43. Expected<int64_t> runAndMeasure(const char *Counters) const override {
  44. auto ResultOrError = runAndSample(Counters);
  45. if (ResultOrError)
  46. return ResultOrError.get()[0];
  47. return ResultOrError.takeError();
  48. }
  49. static void
  50. accumulateCounterValues(const llvm::SmallVector<int64_t, 4> &NewValues,
  51. llvm::SmallVector<int64_t, 4> *Result) {
  52. const size_t NumValues = std::max(NewValues.size(), Result->size());
  53. if (NumValues > Result->size())
  54. Result->resize(NumValues, 0);
  55. for (size_t I = 0, End = NewValues.size(); I < End; ++I)
  56. (*Result)[I] += NewValues[I];
  57. }
  58. Expected<llvm::SmallVector<int64_t, 4>>
  59. runAndSample(const char *Counters) const override {
  60. // We sum counts when there are several counters for a single ProcRes
  61. // (e.g. P23 on SandyBridge).
  62. llvm::SmallVector<int64_t, 4> CounterValues;
  63. int Reserved = 0;
  64. SmallVector<StringRef, 2> CounterNames;
  65. StringRef(Counters).split(CounterNames, '+');
  66. char *const ScratchPtr = Scratch->ptr();
  67. const ExegesisTarget &ET = State.getExegesisTarget();
  68. for (auto &CounterName : CounterNames) {
  69. CounterName = CounterName.trim();
  70. auto CounterOrError = ET.createCounter(CounterName, State);
  71. if (!CounterOrError)
  72. return CounterOrError.takeError();
  73. pfm::Counter *Counter = CounterOrError.get().get();
  74. if (Reserved == 0) {
  75. Reserved = Counter->numValues();
  76. CounterValues.reserve(Reserved);
  77. } else if (Reserved != Counter->numValues())
  78. // It'd be wrong to accumulate vectors of different sizes.
  79. return make_error<Failure>(
  80. llvm::Twine("Inconsistent number of values for counter ")
  81. .concat(CounterName)
  82. .concat(std::to_string(Counter->numValues()))
  83. .concat(" vs expected of ")
  84. .concat(std::to_string(Reserved)));
  85. Scratch->clear();
  86. {
  87. auto PS = ET.withSavedState();
  88. CrashRecoveryContext CRC;
  89. CrashRecoveryContext::Enable();
  90. const bool Crashed = !CRC.RunSafely([this, Counter, ScratchPtr]() {
  91. Counter->start();
  92. this->Function(ScratchPtr);
  93. Counter->stop();
  94. });
  95. CrashRecoveryContext::Disable();
  96. PS.reset();
  97. if (Crashed) {
  98. std::string Msg = "snippet crashed while running";
  99. #ifdef LLVM_ON_UNIX
  100. // See "Exit Status for Commands":
  101. // https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html
  102. constexpr const int kSigOffset = 128;
  103. if (const char *const SigName = strsignal(CRC.RetCode - kSigOffset)) {
  104. Msg += ": ";
  105. Msg += SigName;
  106. }
  107. #endif
  108. return make_error<SnippetCrash>(std::move(Msg));
  109. }
  110. }
  111. auto ValueOrError = Counter->readOrError(Function.getFunctionBytes());
  112. if (!ValueOrError)
  113. return ValueOrError.takeError();
  114. accumulateCounterValues(ValueOrError.get(), &CounterValues);
  115. }
  116. return CounterValues;
  117. }
  118. const LLVMState &State;
  119. const ExecutableFunction Function;
  120. BenchmarkRunner::ScratchSpace *const Scratch;
  121. };
  122. } // namespace
  123. Expected<SmallString<0>> BenchmarkRunner::assembleSnippet(
  124. const BenchmarkCode &BC, const SnippetRepetitor &Repetitor,
  125. unsigned MinInstructions, unsigned LoopBodySize) const {
  126. const std::vector<MCInst> &Instructions = BC.Key.Instructions;
  127. SmallString<0> Buffer;
  128. raw_svector_ostream OS(Buffer);
  129. if (Error E = assembleToStream(
  130. State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns,
  131. BC.Key.RegisterInitialValues,
  132. Repetitor.Repeat(Instructions, MinInstructions, LoopBodySize), OS)) {
  133. return std::move(E);
  134. }
  135. return Buffer;
  136. }
  137. Expected<BenchmarkRunner::RunnableConfiguration>
  138. BenchmarkRunner::getRunnableConfiguration(
  139. const BenchmarkCode &BC, unsigned NumRepetitions, unsigned LoopBodySize,
  140. const SnippetRepetitor &Repetitor) const {
  141. RunnableConfiguration RC;
  142. InstructionBenchmark &InstrBenchmark = RC.InstrBenchmark;
  143. InstrBenchmark.Mode = Mode;
  144. InstrBenchmark.CpuName = std::string(State.getTargetMachine().getTargetCPU());
  145. InstrBenchmark.LLVMTriple =
  146. State.getTargetMachine().getTargetTriple().normalize();
  147. InstrBenchmark.NumRepetitions = NumRepetitions;
  148. InstrBenchmark.Info = BC.Info;
  149. const std::vector<MCInst> &Instructions = BC.Key.Instructions;
  150. InstrBenchmark.Key = BC.Key;
  151. // Assemble at least kMinInstructionsForSnippet instructions by repeating
  152. // the snippet for debug/analysis. This is so that the user clearly
  153. // understands that the inside instructions are repeated.
  154. if (BenchmarkPhaseSelector > BenchmarkPhaseSelectorE::PrepareSnippet) {
  155. const int MinInstructionsForSnippet = 4 * Instructions.size();
  156. const int LoopBodySizeForSnippet = 2 * Instructions.size();
  157. auto Snippet = assembleSnippet(BC, Repetitor, MinInstructionsForSnippet,
  158. LoopBodySizeForSnippet);
  159. if (Error E = Snippet.takeError())
  160. return std::move(E);
  161. const ExecutableFunction EF(State.createTargetMachine(),
  162. getObjectFromBuffer(*Snippet));
  163. const auto FnBytes = EF.getFunctionBytes();
  164. llvm::append_range(InstrBenchmark.AssembledSnippet, FnBytes);
  165. }
  166. // Assemble NumRepetitions instructions repetitions of the snippet for
  167. // measurements.
  168. if (BenchmarkPhaseSelector > BenchmarkPhaseSelectorE::PrepareAndAssembleSnippet) {
  169. auto Snippet = assembleSnippet(BC, Repetitor, InstrBenchmark.NumRepetitions,
  170. LoopBodySize);
  171. if (Error E = Snippet.takeError())
  172. return std::move(E);
  173. RC.ObjectFile = getObjectFromBuffer(*Snippet);
  174. }
  175. return std::move(RC);
  176. }
  177. Expected<InstructionBenchmark>
  178. BenchmarkRunner::runConfiguration(RunnableConfiguration &&RC,
  179. bool DumpObjectToDisk) const {
  180. InstructionBenchmark &InstrBenchmark = RC.InstrBenchmark;
  181. object::OwningBinary<object::ObjectFile> &ObjectFile = RC.ObjectFile;
  182. if (DumpObjectToDisk &&
  183. BenchmarkPhaseSelector > BenchmarkPhaseSelectorE::PrepareAndAssembleSnippet) {
  184. auto ObjectFilePath = writeObjectFile(ObjectFile.getBinary()->getData());
  185. if (Error E = ObjectFilePath.takeError()) {
  186. InstrBenchmark.Error = toString(std::move(E));
  187. return std::move(InstrBenchmark);
  188. }
  189. outs() << "Check generated assembly with: /usr/bin/objdump -d "
  190. << *ObjectFilePath << "\n";
  191. }
  192. if (BenchmarkPhaseSelector < BenchmarkPhaseSelectorE::Measure) {
  193. InstrBenchmark.Error = "actual measurements skipped.";
  194. return std::move(InstrBenchmark);
  195. }
  196. const FunctionExecutorImpl Executor(State, std::move(ObjectFile),
  197. Scratch.get());
  198. auto NewMeasurements = runMeasurements(Executor);
  199. if (Error E = NewMeasurements.takeError()) {
  200. if (!E.isA<SnippetCrash>())
  201. return std::move(E);
  202. InstrBenchmark.Error = toString(std::move(E));
  203. return std::move(InstrBenchmark);
  204. }
  205. assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
  206. for (BenchmarkMeasure &BM : *NewMeasurements) {
  207. // Scale the measurements by instruction.
  208. BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
  209. // Scale the measurements by snippet.
  210. BM.PerSnippetValue *=
  211. static_cast<double>(InstrBenchmark.Key.Instructions.size()) /
  212. InstrBenchmark.NumRepetitions;
  213. }
  214. InstrBenchmark.Measurements = std::move(*NewMeasurements);
  215. return std::move(InstrBenchmark);
  216. }
  217. Expected<std::string> BenchmarkRunner::writeObjectFile(StringRef Buffer) 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. OFS.write(Buffer.data(), Buffer.size());
  225. OFS.flush();
  226. return std::string(ResultPath.str());
  227. }
  228. BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
  229. } // namespace exegesis
  230. } // namespace llvm