BenchmarkRunner.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. //===-- BenchmarkRunner.cpp -------------------------------------*- C++ -*-===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. #include <array>
  10. #include <string>
  11. #include "Assembler.h"
  12. #include "BenchmarkRunner.h"
  13. #include "MCInstrDescView.h"
  14. #include "PerfHelper.h"
  15. #include "llvm/ADT/StringExtras.h"
  16. #include "llvm/ADT/StringRef.h"
  17. #include "llvm/ADT/Twine.h"
  18. #include "llvm/Support/CrashRecoveryContext.h"
  19. #include "llvm/Support/FileSystem.h"
  20. #include "llvm/Support/MemoryBuffer.h"
  21. #include "llvm/Support/Program.h"
  22. namespace llvm {
  23. namespace exegesis {
  24. BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S)
  25. : llvm::StringError(S, llvm::inconvertibleErrorCode()) {}
  26. BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
  27. InstructionBenchmark::ModeE Mode)
  28. : State(State), Mode(Mode), Scratch(llvm::make_unique<ScratchSpace>()) {}
  29. BenchmarkRunner::~BenchmarkRunner() = default;
  30. // Repeat the snippet until there are at least MinInstructions in the resulting
  31. // code.
  32. static std::vector<llvm::MCInst>
  33. GenerateInstructions(const BenchmarkCode &BC, const size_t MinInstructions) {
  34. if (BC.Instructions.empty())
  35. return {};
  36. std::vector<llvm::MCInst> Code = BC.Instructions;
  37. for (int I = 0; Code.size() < MinInstructions; ++I)
  38. Code.push_back(BC.Instructions[I % BC.Instructions.size()]);
  39. return Code;
  40. }
  41. namespace {
  42. class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
  43. public:
  44. FunctionExecutorImpl(const LLVMState &State,
  45. llvm::object::OwningBinary<llvm::object::ObjectFile> Obj,
  46. BenchmarkRunner::ScratchSpace *Scratch)
  47. : Function(State.createTargetMachine(), std::move(Obj)),
  48. Scratch(Scratch) {}
  49. private:
  50. llvm::Expected<int64_t> runAndMeasure(const char *Counters) const override {
  51. // We sum counts when there are several counters for a single ProcRes
  52. // (e.g. P23 on SandyBridge).
  53. int64_t CounterValue = 0;
  54. llvm::SmallVector<llvm::StringRef, 2> CounterNames;
  55. llvm::StringRef(Counters).split(CounterNames, '+');
  56. char *const ScratchPtr = Scratch->ptr();
  57. for (auto &CounterName : CounterNames) {
  58. CounterName = CounterName.trim();
  59. pfm::PerfEvent PerfEvent(CounterName);
  60. if (!PerfEvent.valid())
  61. llvm::report_fatal_error(
  62. llvm::Twine("invalid perf event '").concat(CounterName).concat("'"));
  63. pfm::Counter Counter(PerfEvent);
  64. Scratch->clear();
  65. {
  66. llvm::CrashRecoveryContext CRC;
  67. llvm::CrashRecoveryContext::Enable();
  68. const bool Crashed = !CRC.RunSafely([this, &Counter, ScratchPtr]() {
  69. Counter.start();
  70. this->Function(ScratchPtr);
  71. Counter.stop();
  72. });
  73. llvm::CrashRecoveryContext::Disable();
  74. // FIXME: Better diagnosis.
  75. if (Crashed)
  76. return llvm::make_error<BenchmarkFailure>(
  77. "snippet crashed while running");
  78. }
  79. CounterValue += Counter.read();
  80. }
  81. return CounterValue;
  82. }
  83. const ExecutableFunction Function;
  84. BenchmarkRunner::ScratchSpace *const Scratch;
  85. };
  86. } // namespace
  87. InstructionBenchmark
  88. BenchmarkRunner::runConfiguration(const BenchmarkCode &BC,
  89. unsigned NumRepetitions) const {
  90. InstructionBenchmark InstrBenchmark;
  91. InstrBenchmark.Mode = Mode;
  92. InstrBenchmark.CpuName = State.getTargetMachine().getTargetCPU();
  93. InstrBenchmark.LLVMTriple =
  94. State.getTargetMachine().getTargetTriple().normalize();
  95. InstrBenchmark.NumRepetitions = NumRepetitions;
  96. InstrBenchmark.Info = BC.Info;
  97. const std::vector<llvm::MCInst> &Instructions = BC.Instructions;
  98. InstrBenchmark.Key.Instructions = Instructions;
  99. InstrBenchmark.Key.RegisterInitialValues = BC.RegisterInitialValues;
  100. // Assemble at least kMinInstructionsForSnippet instructions by repeating the
  101. // snippet for debug/analysis. This is so that the user clearly understands
  102. // that the inside instructions are repeated.
  103. constexpr const int kMinInstructionsForSnippet = 16;
  104. {
  105. auto ObjectFilePath = writeObjectFile(
  106. BC, GenerateInstructions(BC, kMinInstructionsForSnippet));
  107. if (llvm::Error E = ObjectFilePath.takeError()) {
  108. InstrBenchmark.Error = llvm::toString(std::move(E));
  109. return InstrBenchmark;
  110. }
  111. const ExecutableFunction EF(State.createTargetMachine(),
  112. getObjectFromFile(*ObjectFilePath));
  113. const auto FnBytes = EF.getFunctionBytes();
  114. InstrBenchmark.AssembledSnippet.assign(FnBytes.begin(), FnBytes.end());
  115. }
  116. // Assemble NumRepetitions instructions repetitions of the snippet for
  117. // measurements.
  118. auto ObjectFilePath = writeObjectFile(
  119. BC, GenerateInstructions(BC, InstrBenchmark.NumRepetitions));
  120. if (llvm::Error E = ObjectFilePath.takeError()) {
  121. InstrBenchmark.Error = llvm::toString(std::move(E));
  122. return InstrBenchmark;
  123. }
  124. llvm::outs() << "Check generated assembly with: /usr/bin/objdump -d "
  125. << *ObjectFilePath << "\n";
  126. const FunctionExecutorImpl Executor(State, getObjectFromFile(*ObjectFilePath),
  127. Scratch.get());
  128. auto Measurements = runMeasurements(Executor);
  129. if (llvm::Error E = Measurements.takeError()) {
  130. InstrBenchmark.Error = llvm::toString(std::move(E));
  131. return InstrBenchmark;
  132. }
  133. InstrBenchmark.Measurements = std::move(*Measurements);
  134. assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
  135. for (BenchmarkMeasure &BM : InstrBenchmark.Measurements) {
  136. // Scale the measurements by instruction.
  137. BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
  138. // Scale the measurements by snippet.
  139. BM.PerSnippetValue *= static_cast<double>(BC.Instructions.size()) /
  140. InstrBenchmark.NumRepetitions;
  141. }
  142. return InstrBenchmark;
  143. }
  144. llvm::Expected<std::string>
  145. BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
  146. llvm::ArrayRef<llvm::MCInst> Code) const {
  147. int ResultFD = 0;
  148. llvm::SmallString<256> ResultPath;
  149. if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile(
  150. "snippet", "o", ResultFD, ResultPath)))
  151. return std::move(E);
  152. llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
  153. assembleToStream(State.getExegesisTarget(), State.createTargetMachine(),
  154. BC.LiveIns, BC.ScratchRegisterCopies, BC.RegisterInitialValues, Code, OFS);
  155. return ResultPath.str();
  156. }
  157. BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
  158. } // namespace exegesis
  159. } // namespace llvm