mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-30 23:21:04 +00:00
[llvm-exegesis] Allow measuring several instructions in a single run.
Summary: We try to recover gracefully on instructions that would crash the program. This includes some refactoring of runMeasurement() implementations. Reviewers: gchatelet Subscribers: tschuett, llvm-commits Differential Revision: https://reviews.llvm.org/D53371 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@344695 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
0cb92ac202
commit
0a1aef0095
@ -175,9 +175,10 @@ OPTIONS
|
||||
Specify the opcode to measure, by index. See example 1 for details.
|
||||
Either `opcode-index`, `opcode-name` or `snippets-file` must be set.
|
||||
|
||||
.. option:: -opcode-name=<LLVM opcode name>
|
||||
.. option:: -opcode-name=<opcode name 1>,<opcode name 2>,...
|
||||
|
||||
Specify the opcode to measure, by name. See example 1 for details.
|
||||
Specify the opcode to measure, by name. Several opcodes can be specified as
|
||||
a comma-separated list. See example 1 for details.
|
||||
Either `opcode-index`, `opcode-name` or `snippets-file` must be set.
|
||||
|
||||
.. option:: -snippets-file=<filename>
|
||||
|
@ -13,9 +13,11 @@
|
||||
#include "Assembler.h"
|
||||
#include "BenchmarkRunner.h"
|
||||
#include "MCInstrDescView.h"
|
||||
#include "PerfHelper.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/CrashRecoveryContext.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
@ -43,6 +45,54 @@ GenerateInstructions(const BenchmarkCode &BC, const size_t MinInstructions) {
|
||||
return Code;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
|
||||
public:
|
||||
FunctionExecutorImpl(const LLVMState &State,
|
||||
llvm::object::OwningBinary<llvm::object::ObjectFile> Obj,
|
||||
BenchmarkRunner::ScratchSpace *Scratch)
|
||||
: Function(State.createTargetMachine(), std::move(Obj)),
|
||||
Scratch(Scratch) {}
|
||||
|
||||
private:
|
||||
llvm::Expected<int64_t> runAndMeasure(const char *Counters) const override {
|
||||
// We sum counts when there are several counters for a single ProcRes
|
||||
// (e.g. P23 on SandyBridge).
|
||||
int64_t CounterValue = 0;
|
||||
llvm::SmallVector<llvm::StringRef, 2> CounterNames;
|
||||
llvm::StringRef(Counters).split(CounterNames, ',');
|
||||
char *const ScratchPtr = Scratch->ptr();
|
||||
for (const auto &CounterName : CounterNames) {
|
||||
pfm::PerfEvent PerfEvent(CounterName);
|
||||
if (!PerfEvent.valid())
|
||||
llvm::report_fatal_error(
|
||||
llvm::Twine("invalid perf event ").concat(Counters));
|
||||
pfm::Counter Counter(PerfEvent);
|
||||
Scratch->clear();
|
||||
{
|
||||
llvm::CrashRecoveryContext CRC;
|
||||
llvm::CrashRecoveryContext::Enable();
|
||||
const bool Crashed = !CRC.RunSafely([this, &Counter, ScratchPtr]() {
|
||||
Counter.start();
|
||||
Function(ScratchPtr);
|
||||
Counter.stop();
|
||||
});
|
||||
llvm::CrashRecoveryContext::Disable();
|
||||
// FIXME: Better diagnosis.
|
||||
if (Crashed)
|
||||
return llvm::make_error<BenchmarkFailure>(
|
||||
"snippet crashed while running");
|
||||
}
|
||||
CounterValue += Counter.read();
|
||||
}
|
||||
return CounterValue;
|
||||
}
|
||||
|
||||
const ExecutableFunction Function;
|
||||
BenchmarkRunner::ScratchSpace *const Scratch;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
InstructionBenchmark
|
||||
BenchmarkRunner::runConfiguration(const BenchmarkCode &BC,
|
||||
unsigned NumRepetitions) const {
|
||||
@ -86,16 +136,21 @@ BenchmarkRunner::runConfiguration(const BenchmarkCode &BC,
|
||||
}
|
||||
llvm::outs() << "Check generated assembly with: /usr/bin/objdump -d "
|
||||
<< *ObjectFilePath << "\n";
|
||||
const ExecutableFunction EF(State.createTargetMachine(),
|
||||
getObjectFromFile(*ObjectFilePath));
|
||||
InstrBenchmark.Measurements = runMeasurements(EF, *Scratch);
|
||||
const FunctionExecutorImpl Executor(State, getObjectFromFile(*ObjectFilePath),
|
||||
Scratch.get());
|
||||
auto Measurements = runMeasurements(Executor);
|
||||
if (llvm::Error E = Measurements.takeError()) {
|
||||
InstrBenchmark.Error = llvm::toString(std::move(E));
|
||||
return InstrBenchmark;
|
||||
}
|
||||
InstrBenchmark.Measurements = std::move(*Measurements);
|
||||
assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
|
||||
for (BenchmarkMeasure &BM : InstrBenchmark.Measurements) {
|
||||
// Scale the measurements by instruction.
|
||||
BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
|
||||
// Scale the measurements by snippet.
|
||||
BM.PerSnippetValue *= static_cast<double>(BC.Instructions.size()) /
|
||||
InstrBenchmark.NumRepetitions;
|
||||
InstrBenchmark.NumRepetitions;
|
||||
}
|
||||
|
||||
return InstrBenchmark;
|
||||
@ -115,4 +170,6 @@ BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
|
||||
return ResultPath.str();
|
||||
}
|
||||
|
||||
BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
|
||||
|
||||
} // namespace exegesis
|
||||
|
@ -64,13 +64,21 @@ public:
|
||||
char *const AlignedPtr;
|
||||
};
|
||||
|
||||
// A helper to measure counters while executing a function in a sandboxed
|
||||
// context.
|
||||
class FunctionExecutor {
|
||||
public:
|
||||
~FunctionExecutor();
|
||||
virtual llvm::Expected<int64_t>
|
||||
runAndMeasure(const char *Counters) const = 0;
|
||||
};
|
||||
|
||||
protected:
|
||||
const LLVMState &State;
|
||||
|
||||
private:
|
||||
virtual std::vector<BenchmarkMeasure>
|
||||
runMeasurements(const ExecutableFunction &EF,
|
||||
ScratchSpace &Scratch) const = 0;
|
||||
virtual llvm::Expected<std::vector<BenchmarkMeasure>>
|
||||
runMeasurements(const FunctionExecutor &Executor) const = 0;
|
||||
|
||||
llvm::Expected<std::string>
|
||||
writeObjectFile(const BenchmarkCode &Configuration,
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "Assembler.h"
|
||||
#include "BenchmarkRunner.h"
|
||||
#include "MCInstrDescView.h"
|
||||
#include "PerfHelper.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/MC/MCInst.h"
|
||||
#include "llvm/MC/MCInstBuilder.h"
|
||||
@ -178,30 +177,26 @@ const char *LatencyBenchmarkRunner::getCounterName() const {
|
||||
|
||||
LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
|
||||
|
||||
std::vector<BenchmarkMeasure>
|
||||
LatencyBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
|
||||
ScratchSpace &Scratch) const {
|
||||
llvm::Expected<std::vector<BenchmarkMeasure>>
|
||||
LatencyBenchmarkRunner::runMeasurements(
|
||||
const FunctionExecutor &Executor) const {
|
||||
// Cycle measurements include some overhead from the kernel. Repeat the
|
||||
// measure several times and take the minimum value.
|
||||
constexpr const int NumMeasurements = 30;
|
||||
int64_t MinLatency = std::numeric_limits<int64_t>::max();
|
||||
int64_t MinValue = std::numeric_limits<int64_t>::max();
|
||||
const char *CounterName = getCounterName();
|
||||
if (!CounterName)
|
||||
llvm::report_fatal_error("could not determine cycle counter name");
|
||||
const pfm::PerfEvent CyclesPerfEvent(CounterName);
|
||||
if (!CyclesPerfEvent.valid())
|
||||
llvm::report_fatal_error("invalid perf event");
|
||||
for (size_t I = 0; I < NumMeasurements; ++I) {
|
||||
pfm::Counter Counter(CyclesPerfEvent);
|
||||
Scratch.clear();
|
||||
Counter.start();
|
||||
Function(Scratch.ptr());
|
||||
Counter.stop();
|
||||
const int64_t Value = Counter.read();
|
||||
if (Value < MinLatency)
|
||||
MinLatency = Value;
|
||||
auto ExpectedCounterValue = Executor.runAndMeasure(CounterName);
|
||||
if (!ExpectedCounterValue)
|
||||
return ExpectedCounterValue.takeError();
|
||||
if (*ExpectedCounterValue < MinValue)
|
||||
MinValue = *ExpectedCounterValue;
|
||||
}
|
||||
return {BenchmarkMeasure::Create("latency", MinLatency)};
|
||||
std::vector<BenchmarkMeasure> Result = {
|
||||
BenchmarkMeasure::Create("latency", MinValue)};
|
||||
return std::move(Result);
|
||||
}
|
||||
|
||||
} // namespace exegesis
|
||||
|
@ -37,9 +37,8 @@ public:
|
||||
~LatencyBenchmarkRunner() override;
|
||||
|
||||
private:
|
||||
std::vector<BenchmarkMeasure>
|
||||
runMeasurements(const ExecutableFunction &EF,
|
||||
ScratchSpace &Scratch) const override;
|
||||
llvm::Expected<std::vector<BenchmarkMeasure>>
|
||||
runMeasurements(const FunctionExecutor &Executor) const override;
|
||||
|
||||
virtual const char *getCounterName() const;
|
||||
};
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "Assembler.h"
|
||||
#include "BenchmarkRunner.h"
|
||||
#include "MCInstrDescView.h"
|
||||
#include "PerfHelper.h"
|
||||
#include "Target.h"
|
||||
|
||||
// FIXME: Load constants into registers (e.g. with fld1) to not break
|
||||
@ -221,33 +220,10 @@ UopsSnippetGenerator::generateCodeTemplates(const Instruction &Instr) const {
|
||||
return getSingleton(std::move(CT));
|
||||
}
|
||||
|
||||
std::vector<BenchmarkMeasure>
|
||||
UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
|
||||
ScratchSpace &Scratch) const {
|
||||
llvm::Expected<std::vector<BenchmarkMeasure>>
|
||||
UopsBenchmarkRunner::runMeasurements(const FunctionExecutor &Executor) const {
|
||||
const auto &SchedModel = State.getSubtargetInfo().getSchedModel();
|
||||
|
||||
const auto RunMeasurement = [&Function,
|
||||
&Scratch](const char *const Counters) {
|
||||
// We sum counts when there are several counters for a single ProcRes
|
||||
// (e.g. P23 on SandyBridge).
|
||||
int64_t CounterValue = 0;
|
||||
llvm::SmallVector<llvm::StringRef, 2> CounterNames;
|
||||
llvm::StringRef(Counters).split(CounterNames, ',');
|
||||
for (const auto &CounterName : CounterNames) {
|
||||
pfm::PerfEvent UopPerfEvent(CounterName);
|
||||
if (!UopPerfEvent.valid())
|
||||
llvm::report_fatal_error(
|
||||
llvm::Twine("invalid perf event ").concat(Counters));
|
||||
pfm::Counter Counter(UopPerfEvent);
|
||||
Scratch.clear();
|
||||
Counter.start();
|
||||
Function(Scratch.ptr());
|
||||
Counter.stop();
|
||||
CounterValue += Counter.read();
|
||||
}
|
||||
return CounterValue;
|
||||
};
|
||||
|
||||
std::vector<BenchmarkMeasure> Result;
|
||||
const auto &PfmCounters = SchedModel.getExtraProcessorInfo().PfmCounters;
|
||||
// Uops per port.
|
||||
@ -256,16 +232,21 @@ UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function,
|
||||
const char *const Counters = PfmCounters.IssueCounters[ProcResIdx];
|
||||
if (!Counters)
|
||||
continue;
|
||||
const double CounterValue = RunMeasurement(Counters);
|
||||
auto ExpectedCounterValue = Executor.runAndMeasure(Counters);
|
||||
if (!ExpectedCounterValue)
|
||||
return ExpectedCounterValue.takeError();
|
||||
Result.push_back(BenchmarkMeasure::Create(
|
||||
SchedModel.getProcResource(ProcResIdx)->Name, CounterValue));
|
||||
SchedModel.getProcResource(ProcResIdx)->Name, *ExpectedCounterValue));
|
||||
}
|
||||
// NumMicroOps.
|
||||
if (const char *const UopsCounter = PfmCounters.UopsCounter) {
|
||||
const double CounterValue = RunMeasurement(UopsCounter);
|
||||
Result.push_back(BenchmarkMeasure::Create("NumMicroOps", CounterValue));
|
||||
auto ExpectedCounterValue = Executor.runAndMeasure(UopsCounter);
|
||||
if (!ExpectedCounterValue)
|
||||
return ExpectedCounterValue.takeError();
|
||||
Result.push_back(
|
||||
BenchmarkMeasure::Create("NumMicroOps", *ExpectedCounterValue));
|
||||
}
|
||||
return Result;
|
||||
return std::move(Result);
|
||||
}
|
||||
|
||||
constexpr const size_t UopsSnippetGenerator::kMinNumDifferentAddresses;
|
||||
|
@ -68,9 +68,8 @@ public:
|
||||
static constexpr const size_t kMinNumDifferentAddresses = 6;
|
||||
|
||||
private:
|
||||
std::vector<BenchmarkMeasure>
|
||||
runMeasurements(const ExecutableFunction &EF,
|
||||
ScratchSpace &Scratch) const override;
|
||||
llvm::Expected<std::vector<BenchmarkMeasure>>
|
||||
runMeasurements(const FunctionExecutor &Executor) const override;
|
||||
};
|
||||
|
||||
} // namespace exegesis
|
||||
|
@ -38,13 +38,14 @@
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
static llvm::cl::opt<unsigned>
|
||||
static llvm::cl::opt<int>
|
||||
OpcodeIndex("opcode-index", llvm::cl::desc("opcode to measure, by index"),
|
||||
llvm::cl::init(0));
|
||||
|
||||
static llvm::cl::opt<std::string>
|
||||
OpcodeName("opcode-name", llvm::cl::desc("opcode to measure, by name"),
|
||||
llvm::cl::init(""));
|
||||
static llvm::cl::opt<std::string> OpcodeNames(
|
||||
"opcode-name",
|
||||
llvm::cl::desc("comma-separated list of opcodes to measure, by name"),
|
||||
llvm::cl::init(""));
|
||||
|
||||
static llvm::cl::opt<std::string>
|
||||
SnippetsFile("snippets-file", llvm::cl::desc("code snippets to measure"),
|
||||
@ -99,11 +100,12 @@ static llvm::ExitOnError ExitOnErr;
|
||||
void LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET();
|
||||
#endif
|
||||
|
||||
// Checks that only one of OpcodeName, OpcodeIndex or SnippetsFile is provided,
|
||||
// and returns the opcode index or 0 if snippets should be read from
|
||||
// Checks that only one of OpcodeNames, OpcodeIndex or SnippetsFile is provided,
|
||||
// and returns the opcode indices or {} if snippets should be read from
|
||||
// `SnippetsFile`.
|
||||
static unsigned getOpcodeOrDie(const llvm::MCInstrInfo &MCInstrInfo) {
|
||||
const size_t NumSetFlags = (OpcodeName.empty() ? 0 : 1) +
|
||||
static std::vector<unsigned>
|
||||
getOpcodesOrDie(const llvm::MCInstrInfo &MCInstrInfo) {
|
||||
const size_t NumSetFlags = (OpcodeNames.empty() ? 0 : 1) +
|
||||
(OpcodeIndex == 0 ? 0 : 1) +
|
||||
(SnippetsFile.empty() ? 0 : 1);
|
||||
if (NumSetFlags != 1)
|
||||
@ -111,14 +113,35 @@ static unsigned getOpcodeOrDie(const llvm::MCInstrInfo &MCInstrInfo) {
|
||||
"please provide one and only one of 'opcode-index', 'opcode-name' or "
|
||||
"'snippets-file'");
|
||||
if (!SnippetsFile.empty())
|
||||
return 0;
|
||||
return {};
|
||||
if (OpcodeIndex > 0)
|
||||
return OpcodeIndex;
|
||||
return {static_cast<unsigned>(OpcodeIndex)};
|
||||
if (OpcodeIndex < 0) {
|
||||
std::vector<unsigned> Result;
|
||||
for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I <= E; ++I)
|
||||
Result.push_back(I);
|
||||
return Result;
|
||||
}
|
||||
// Resolve opcode name -> opcode.
|
||||
for (unsigned I = 0, E = MCInstrInfo.getNumOpcodes(); I < E; ++I)
|
||||
if (MCInstrInfo.getName(I) == OpcodeName)
|
||||
return I;
|
||||
llvm::report_fatal_error(llvm::Twine("unknown opcode ").concat(OpcodeName));
|
||||
const auto ResolveName =
|
||||
[&MCInstrInfo](llvm::StringRef OpcodeName) -> unsigned {
|
||||
for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I < E; ++I)
|
||||
if (MCInstrInfo.getName(I) == OpcodeName)
|
||||
return I;
|
||||
return 0u;
|
||||
};
|
||||
llvm::SmallVector<llvm::StringRef, 2> Pieces;
|
||||
llvm::StringRef(OpcodeNames.getValue())
|
||||
.split(Pieces, ",", /* MaxSplit */ -1, /* KeepEmpty */ false);
|
||||
std::vector<unsigned> Result;
|
||||
for (const llvm::StringRef OpcodeName : Pieces) {
|
||||
if (unsigned Opcode = ResolveName(OpcodeName))
|
||||
Result.push_back(Opcode);
|
||||
else
|
||||
llvm::report_fatal_error(
|
||||
llvm::Twine("unknown opcode ").concat(OpcodeName));
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Generates code snippets for opcode `Opcode`.
|
||||
@ -299,18 +322,29 @@ void benchmarkMain() {
|
||||
#endif
|
||||
|
||||
const LLVMState State;
|
||||
const auto Opcode = getOpcodeOrDie(State.getInstrInfo());
|
||||
const auto Opcodes = getOpcodesOrDie(State.getInstrInfo());
|
||||
|
||||
std::vector<BenchmarkCode> Configurations;
|
||||
if (Opcode > 0) {
|
||||
// Ignore instructions without a sched class if -ignore-invalid-sched-class
|
||||
// is passed.
|
||||
if (IgnoreInvalidSchedClass &&
|
||||
State.getInstrInfo().get(Opcode).getSchedClass() == 0) {
|
||||
llvm::errs() << "ignoring instruction without sched class\n";
|
||||
return;
|
||||
if (!Opcodes.empty()) {
|
||||
for (const unsigned Opcode : Opcodes) {
|
||||
// Ignore instructions without a sched class if
|
||||
// -ignore-invalid-sched-class is passed.
|
||||
if (IgnoreInvalidSchedClass &&
|
||||
State.getInstrInfo().get(Opcode).getSchedClass() == 0) {
|
||||
llvm::errs() << State.getInstrInfo().getName(Opcode)
|
||||
<< ": ignoring instruction without sched class\n";
|
||||
continue;
|
||||
}
|
||||
auto ConfigsForInstr = generateSnippets(State, Opcode);
|
||||
if (!ConfigsForInstr) {
|
||||
llvm::logAllUnhandledErrors(
|
||||
ConfigsForInstr.takeError(), llvm::errs(),
|
||||
llvm::Twine(State.getInstrInfo().getName(Opcode)).concat(": "));
|
||||
continue;
|
||||
}
|
||||
std::move(ConfigsForInstr->begin(), ConfigsForInstr->end(),
|
||||
std::back_inserter(Configurations));
|
||||
}
|
||||
Configurations = ExitOnErr(generateSnippets(State, Opcode));
|
||||
} else {
|
||||
Configurations = ExitOnErr(readSnippets(State, SnippetsFile));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user