BenchmarkRunner.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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, unsigned LoopBodySize,
  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. const int MinInstructionsForSnippet = 4 * Instructions.size();
  153. const int LoopBodySizeForSnippet = 2 * Instructions.size();
  154. {
  155. SmallString<0> Buffer;
  156. raw_svector_ostream OS(Buffer);
  157. if (Error E = assembleToStream(
  158. State.getExegesisTarget(), State.createTargetMachine(),
  159. BC.LiveIns, BC.Key.RegisterInitialValues,
  160. Repetitor->Repeat(Instructions, MinInstructionsForSnippet,
  161. LoopBodySizeForSnippet),
  162. OS)) {
  163. return std::move(E);
  164. }
  165. const ExecutableFunction EF(State.createTargetMachine(),
  166. getObjectFromBuffer(OS.str()));
  167. const auto FnBytes = EF.getFunctionBytes();
  168. llvm::append_range(InstrBenchmark.AssembledSnippet, FnBytes);
  169. }
  170. // Assemble NumRepetitions instructions repetitions of the snippet for
  171. // measurements.
  172. const auto Filler = Repetitor->Repeat(
  173. Instructions, InstrBenchmark.NumRepetitions, LoopBodySize);
  174. object::OwningBinary<object::ObjectFile> ObjectFile;
  175. if (DumpObjectToDisk) {
  176. auto ObjectFilePath = writeObjectFile(BC, Filler);
  177. if (Error E = ObjectFilePath.takeError()) {
  178. InstrBenchmark.Error = toString(std::move(E));
  179. return InstrBenchmark;
  180. }
  181. outs() << "Check generated assembly with: /usr/bin/objdump -d "
  182. << *ObjectFilePath << "\n";
  183. ObjectFile = getObjectFromFile(*ObjectFilePath);
  184. } else {
  185. SmallString<0> Buffer;
  186. raw_svector_ostream OS(Buffer);
  187. if (Error E = assembleToStream(
  188. State.getExegesisTarget(), State.createTargetMachine(),
  189. BC.LiveIns, BC.Key.RegisterInitialValues, Filler, OS)) {
  190. return std::move(E);
  191. }
  192. ObjectFile = getObjectFromBuffer(OS.str());
  193. }
  194. const FunctionExecutorImpl Executor(State, std::move(ObjectFile),
  195. Scratch.get());
  196. auto NewMeasurements = runMeasurements(Executor);
  197. if (Error E = NewMeasurements.takeError()) {
  198. if (!E.isA<SnippetCrash>())
  199. return std::move(E);
  200. InstrBenchmark.Error = toString(std::move(E));
  201. return InstrBenchmark;
  202. }
  203. assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
  204. for (BenchmarkMeasure &BM : *NewMeasurements) {
  205. // Scale the measurements by instruction.
  206. BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
  207. // Scale the measurements by snippet.
  208. BM.PerSnippetValue *= static_cast<double>(Instructions.size()) /
  209. InstrBenchmark.NumRepetitions;
  210. }
  211. if (InstrBenchmark.Measurements.empty()) {
  212. InstrBenchmark.Measurements = std::move(*NewMeasurements);
  213. continue;
  214. }
  215. assert(Repetitors.size() > 1 && !InstrBenchmark.Measurements.empty() &&
  216. "We're in an 'min' repetition mode, and need to aggregate new "
  217. "result to the existing result.");
  218. assert(InstrBenchmark.Measurements.size() == NewMeasurements->size() &&
  219. "Expected to have identical number of measurements.");
  220. for (auto I : zip(InstrBenchmark.Measurements, *NewMeasurements)) {
  221. BenchmarkMeasure &Measurement = std::get<0>(I);
  222. BenchmarkMeasure &NewMeasurement = std::get<1>(I);
  223. assert(Measurement.Key == NewMeasurement.Key &&
  224. "Expected measurements to be symmetric");
  225. Measurement.PerInstructionValue = std::min(
  226. Measurement.PerInstructionValue, NewMeasurement.PerInstructionValue);
  227. Measurement.PerSnippetValue =
  228. std::min(Measurement.PerSnippetValue, NewMeasurement.PerSnippetValue);
  229. }
  230. }
  231. // We successfully measured everything, so don't discard the results.
  232. CBOR.disarm();
  233. return InstrBenchmark;
  234. }
  235. Expected<std::string>
  236. BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
  237. const FillFunction &FillFunction) const {
  238. int ResultFD = 0;
  239. SmallString<256> ResultPath;
  240. if (Error E = errorCodeToError(
  241. sys::fs::createTemporaryFile("snippet", "o", ResultFD, ResultPath)))
  242. return std::move(E);
  243. raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
  244. if (Error E = assembleToStream(
  245. State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns,
  246. BC.Key.RegisterInitialValues, FillFunction, OFS)) {
  247. return std::move(E);
  248. }
  249. return std::string(ResultPath.str());
  250. }
  251. BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
  252. } // namespace exegesis
  253. } // namespace llvm