123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- //===-- BenchmarkRunner.cpp -------------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include <array>
- #include <memory>
- #include <string>
- #include "Assembler.h"
- #include "BenchmarkRunner.h"
- #include "Error.h"
- #include "MCInstrDescView.h"
- #include "PerfHelper.h"
- #include "Target.h"
- #include "llvm/ADT/ScopeExit.h"
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/ADT/StringRef.h"
- #include "llvm/ADT/Twine.h"
- #include "llvm/Support/CrashRecoveryContext.h"
- #include "llvm/Support/Error.h"
- #include "llvm/Support/FileSystem.h"
- #include "llvm/Support/MemoryBuffer.h"
- #include "llvm/Support/Program.h"
- namespace llvm {
- namespace exegesis {
- BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
- InstructionBenchmark::ModeE Mode)
- : State(State), Mode(Mode), Scratch(std::make_unique<ScratchSpace>()) {}
- BenchmarkRunner::~BenchmarkRunner() = default;
- namespace {
- class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
- public:
- FunctionExecutorImpl(const LLVMState &State,
- object::OwningBinary<object::ObjectFile> Obj,
- BenchmarkRunner::ScratchSpace *Scratch)
- : State(State), Function(State.createTargetMachine(), std::move(Obj)),
- Scratch(Scratch) {}
- private:
- Expected<int64_t> runAndMeasure(const char *Counters) const override {
- auto ResultOrError = runAndSample(Counters);
- if (ResultOrError)
- return ResultOrError.get()[0];
- return ResultOrError.takeError();
- }
- static void
- accumulateCounterValues(const llvm::SmallVector<int64_t, 4> &NewValues,
- llvm::SmallVector<int64_t, 4> *Result) {
- const size_t NumValues = std::max(NewValues.size(), Result->size());
- if (NumValues > Result->size())
- Result->resize(NumValues, 0);
- for (size_t I = 0, End = NewValues.size(); I < End; ++I)
- (*Result)[I] += NewValues[I];
- }
- Expected<llvm::SmallVector<int64_t, 4>>
- runAndSample(const char *Counters) const override {
- // We sum counts when there are several counters for a single ProcRes
- // (e.g. P23 on SandyBridge).
- llvm::SmallVector<int64_t, 4> CounterValues;
- int Reserved = 0;
- SmallVector<StringRef, 2> CounterNames;
- StringRef(Counters).split(CounterNames, '+');
- char *const ScratchPtr = Scratch->ptr();
- const ExegesisTarget &ET = State.getExegesisTarget();
- for (auto &CounterName : CounterNames) {
- CounterName = CounterName.trim();
- auto CounterOrError = ET.createCounter(CounterName, State);
- if (!CounterOrError)
- return CounterOrError.takeError();
- pfm::Counter *Counter = CounterOrError.get().get();
- if (Reserved == 0) {
- Reserved = Counter->numValues();
- CounterValues.reserve(Reserved);
- } else if (Reserved != Counter->numValues())
- // It'd be wrong to accumulate vectors of different sizes.
- return make_error<Failure>(
- llvm::Twine("Inconsistent number of values for counter ")
- .concat(CounterName)
- .concat(std::to_string(Counter->numValues()))
- .concat(" vs expected of ")
- .concat(std::to_string(Reserved)));
- Scratch->clear();
- {
- auto PS = ET.withSavedState();
- CrashRecoveryContext CRC;
- CrashRecoveryContext::Enable();
- const bool Crashed = !CRC.RunSafely([this, Counter, ScratchPtr]() {
- Counter->start();
- this->Function(ScratchPtr);
- Counter->stop();
- });
- CrashRecoveryContext::Disable();
- PS.reset();
- if (Crashed) {
- std::string Msg = "snippet crashed while running";
- #ifdef LLVM_ON_UNIX
- // See "Exit Status for Commands":
- // https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html
- constexpr const int kSigOffset = 128;
- if (const char *const SigName = strsignal(CRC.RetCode - kSigOffset)) {
- Msg += ": ";
- Msg += SigName;
- }
- #endif
- return make_error<SnippetCrash>(std::move(Msg));
- }
- }
- auto ValueOrError = Counter->readOrError(Function.getFunctionBytes());
- if (!ValueOrError)
- return ValueOrError.takeError();
- accumulateCounterValues(ValueOrError.get(), &CounterValues);
- }
- return CounterValues;
- }
- const LLVMState &State;
- const ExecutableFunction Function;
- BenchmarkRunner::ScratchSpace *const Scratch;
- };
- } // namespace
- Expected<InstructionBenchmark> BenchmarkRunner::runConfiguration(
- const BenchmarkCode &BC, unsigned NumRepetitions, unsigned LoopBodySize,
- ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors,
- bool DumpObjectToDisk) const {
- InstructionBenchmark InstrBenchmark;
- InstrBenchmark.Mode = Mode;
- InstrBenchmark.CpuName = std::string(State.getTargetMachine().getTargetCPU());
- InstrBenchmark.LLVMTriple =
- State.getTargetMachine().getTargetTriple().normalize();
- InstrBenchmark.NumRepetitions = NumRepetitions;
- InstrBenchmark.Info = BC.Info;
- const std::vector<MCInst> &Instructions = BC.Key.Instructions;
- InstrBenchmark.Key = BC.Key;
- // If we end up having an error, and we've previously succeeded with
- // some other Repetitor, we want to discard the previous measurements.
- struct ClearBenchmarkOnReturn {
- ClearBenchmarkOnReturn(InstructionBenchmark *IB) : IB(IB) {}
- ~ClearBenchmarkOnReturn() {
- if (Clear)
- IB->Measurements.clear();
- }
- void disarm() { Clear = false; }
- private:
- InstructionBenchmark *const IB;
- bool Clear = true;
- };
- ClearBenchmarkOnReturn CBOR(&InstrBenchmark);
- for (const std::unique_ptr<const SnippetRepetitor> &Repetitor : Repetitors) {
- // Assemble at least kMinInstructionsForSnippet instructions by repeating
- // the snippet for debug/analysis. This is so that the user clearly
- // understands that the inside instructions are repeated.
- const int MinInstructionsForSnippet = 4 * Instructions.size();
- const int LoopBodySizeForSnippet = 2 * Instructions.size();
- {
- SmallString<0> Buffer;
- raw_svector_ostream OS(Buffer);
- if (Error E = assembleToStream(
- State.getExegesisTarget(), State.createTargetMachine(),
- BC.LiveIns, BC.Key.RegisterInitialValues,
- Repetitor->Repeat(Instructions, MinInstructionsForSnippet,
- LoopBodySizeForSnippet),
- OS)) {
- return std::move(E);
- }
- const ExecutableFunction EF(State.createTargetMachine(),
- getObjectFromBuffer(OS.str()));
- const auto FnBytes = EF.getFunctionBytes();
- llvm::append_range(InstrBenchmark.AssembledSnippet, FnBytes);
- }
- // Assemble NumRepetitions instructions repetitions of the snippet for
- // measurements.
- const auto Filler = Repetitor->Repeat(
- Instructions, InstrBenchmark.NumRepetitions, LoopBodySize);
- object::OwningBinary<object::ObjectFile> ObjectFile;
- if (DumpObjectToDisk) {
- auto ObjectFilePath = writeObjectFile(BC, Filler);
- if (Error E = ObjectFilePath.takeError()) {
- InstrBenchmark.Error = toString(std::move(E));
- return InstrBenchmark;
- }
- outs() << "Check generated assembly with: /usr/bin/objdump -d "
- << *ObjectFilePath << "\n";
- ObjectFile = getObjectFromFile(*ObjectFilePath);
- } else {
- SmallString<0> Buffer;
- raw_svector_ostream OS(Buffer);
- if (Error E = assembleToStream(
- State.getExegesisTarget(), State.createTargetMachine(),
- BC.LiveIns, BC.Key.RegisterInitialValues, Filler, OS)) {
- return std::move(E);
- }
- ObjectFile = getObjectFromBuffer(OS.str());
- }
- const FunctionExecutorImpl Executor(State, std::move(ObjectFile),
- Scratch.get());
- auto NewMeasurements = runMeasurements(Executor);
- if (Error E = NewMeasurements.takeError()) {
- if (!E.isA<SnippetCrash>())
- return std::move(E);
- InstrBenchmark.Error = toString(std::move(E));
- return InstrBenchmark;
- }
- assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
- for (BenchmarkMeasure &BM : *NewMeasurements) {
- // Scale the measurements by instruction.
- BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
- // Scale the measurements by snippet.
- BM.PerSnippetValue *= static_cast<double>(Instructions.size()) /
- InstrBenchmark.NumRepetitions;
- }
- if (InstrBenchmark.Measurements.empty()) {
- InstrBenchmark.Measurements = std::move(*NewMeasurements);
- continue;
- }
- assert(Repetitors.size() > 1 && !InstrBenchmark.Measurements.empty() &&
- "We're in an 'min' repetition mode, and need to aggregate new "
- "result to the existing result.");
- assert(InstrBenchmark.Measurements.size() == NewMeasurements->size() &&
- "Expected to have identical number of measurements.");
- for (auto I : zip(InstrBenchmark.Measurements, *NewMeasurements)) {
- BenchmarkMeasure &Measurement = std::get<0>(I);
- BenchmarkMeasure &NewMeasurement = std::get<1>(I);
- assert(Measurement.Key == NewMeasurement.Key &&
- "Expected measurements to be symmetric");
- Measurement.PerInstructionValue = std::min(
- Measurement.PerInstructionValue, NewMeasurement.PerInstructionValue);
- Measurement.PerSnippetValue =
- std::min(Measurement.PerSnippetValue, NewMeasurement.PerSnippetValue);
- }
- }
- // We successfully measured everything, so don't discard the results.
- CBOR.disarm();
- return InstrBenchmark;
- }
- Expected<std::string>
- BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
- const FillFunction &FillFunction) const {
- int ResultFD = 0;
- SmallString<256> ResultPath;
- if (Error E = errorCodeToError(
- sys::fs::createTemporaryFile("snippet", "o", ResultFD, ResultPath)))
- return std::move(E);
- raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
- if (Error E = assembleToStream(
- State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns,
- BC.Key.RegisterInitialValues, FillFunction, OFS)) {
- return std::move(E);
- }
- return std::string(ResultPath.str());
- }
- BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
- } // namespace exegesis
- } // namespace llvm
|