Skip to content

Commit a39aaf3

Browse files
authored
Reland: "[Exegesis] Add the ability to dry-run the measurement phase (#121991)" (#122775)
This relands f8f8598 Follow up on #122371: The problem here is a little subtle: when we dry-run the measurement phase, we create a LLJIT instance without actually executing the snippets. The key is, LLJIT has its own TargetMachine which uses triple designated by LLVM_TARGET_ARCH (which is default to host). On a machine that does not support Exegesis, the LLJIT would fail to create its TargetMachine because llvm-exegesis don't even register the host's target! Putting this test into any of the target-specific folder won't help, because it's about the host. And personally I don't really want to use `exegesis-can-execute-<arch>` for generic tests like this -- it's too strict as we don't actually need to execute the snippet. My solution here is creating another test feature which is added only when LLVM_TARGET_ARCH is supported by llvm-exegesis. This feature is something in between `<arch>-registered-target` and `exegesis-can-execute-<arch>`.
1 parent a10ce71 commit a39aaf3

File tree

7 files changed

+52
-13
lines changed

7 files changed

+52
-13
lines changed

llvm/docs/CommandGuide/llvm-exegesis.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ OPTIONS
301301
* ``prepare-and-assemble-snippet``: Same as ``prepare-snippet``, but also dumps an excerpt of the sequence (hex encoded).
302302
* ``assemble-measured-code``: Same as ``prepare-and-assemble-snippet``. but also creates the full sequence that can be dumped to a file using ``--dump-object-to-disk``.
303303
* ``measure``: Same as ``assemble-measured-code``, but also runs the measurement.
304+
* ``dry-run-measurement``: Same as measure, but does not actually execute the snippet.
304305

305306
.. option:: --x86-lbr-sample-period=<nBranches/sample>
306307

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# RUN: llvm-exegesis --mtriple=riscv64 --mcpu=sifive-p470 --mode=latency --opcode-name=ADD --use-dummy-perf-counters --benchmark-phase=dry-run-measurement | FileCheck %s
2+
# REQUIRES: riscv-registered-target && native-registered-exegesis-target
3+
4+
# This test makes sure that llvm-exegesis doesn't execute "cross-compiled" snippets in the presence of
5+
# --dry-run-measurement. RISC-V was chosen simply because most of the time we run tests on X86 machines.
6+
7+
# Should not contain misleading results.
8+
# CHECK: measurements: []
9+
10+
# Should not contain error messages like "snippet crashed while running: Segmentation fault".
11+
# CHECK: error: ''

llvm/test/tools/llvm-exegesis/lit.local.cfg

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ def can_use_perf_counters(mode, extra_options=[]):
3030
print("could not exec llvm-exegesis")
3131
return False
3232

33+
# LLJIT builds its own TargetMachine using arch designated by LLVM_TARGET_ARCH, which
34+
# is default to host. We don't want tests that use LLJIT (but not necessarily
35+
# execute the snippets) to run on machines that are not even supported by
36+
# exegesis.
37+
if config.root.native_target in ["AArch64", "Mips", "PowerPC", "RISCV", "X86"]:
38+
config.available_features.add("native-registered-exegesis-target")
3339

3440
for arch in ["aarch64", "mips", "powerpc", "x86_64"]:
3541
if can_execute_generated_snippets(arch):

llvm/tools/llvm-exegesis/lib/BenchmarkResult.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ enum class BenchmarkPhaseSelectorE {
3838
PrepareAndAssembleSnippet,
3939
AssembleMeasuredCode,
4040
Measure,
41+
DryRunMeasure,
4142
};
4243

4344
enum class BenchmarkFilter { All, RegOnly, WithMem };

llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,25 @@ class InProcessFunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
9999
static Expected<std::unique_ptr<InProcessFunctionExecutorImpl>>
100100
create(const LLVMState &State, object::OwningBinary<object::ObjectFile> Obj,
101101
BenchmarkRunner::ScratchSpace *Scratch,
102-
std::optional<int> BenchmarkProcessCPU) {
102+
std::optional<int> BenchmarkProcessCPU, bool DryRun) {
103103
Expected<ExecutableFunction> EF =
104104
ExecutableFunction::create(State.createTargetMachine(), std::move(Obj));
105105

106106
if (!EF)
107107
return EF.takeError();
108108

109109
return std::unique_ptr<InProcessFunctionExecutorImpl>(
110-
new InProcessFunctionExecutorImpl(State, std::move(*EF), Scratch));
110+
new InProcessFunctionExecutorImpl(State, std::move(*EF), Scratch,
111+
DryRun));
111112
}
112113

113114
private:
114115
InProcessFunctionExecutorImpl(const LLVMState &State,
115116
ExecutableFunction Function,
116-
BenchmarkRunner::ScratchSpace *Scratch)
117-
: State(State), Function(std::move(Function)), Scratch(Scratch) {}
117+
BenchmarkRunner::ScratchSpace *Scratch,
118+
bool DryRun)
119+
: State(State), Function(std::move(Function)), Scratch(Scratch),
120+
DryRun(DryRun) {}
118121

119122
static void accumulateCounterValues(const SmallVector<int64_t, 4> &NewValues,
120123
SmallVector<int64_t, 4> *Result) {
@@ -143,9 +146,14 @@ class InProcessFunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
143146
CrashRecoveryContext CRC;
144147
CrashRecoveryContext::Enable();
145148
const bool Crashed = !CRC.RunSafely([this, Counter, ScratchPtr]() {
146-
Counter->start();
147-
this->Function(ScratchPtr);
148-
Counter->stop();
149+
if (DryRun) {
150+
Counter->start();
151+
Counter->stop();
152+
} else {
153+
Counter->start();
154+
this->Function(ScratchPtr);
155+
Counter->stop();
156+
}
149157
});
150158
CrashRecoveryContext::Disable();
151159
PS.reset();
@@ -177,6 +185,7 @@ class InProcessFunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
177185
const LLVMState &State;
178186
const ExecutableFunction Function;
179187
BenchmarkRunner::ScratchSpace *const Scratch;
188+
bool DryRun = false;
180189
};
181190

182191
#ifdef __linux__
@@ -664,21 +673,29 @@ Expected<std::unique_ptr<BenchmarkRunner::FunctionExecutor>>
664673
BenchmarkRunner::createFunctionExecutor(
665674
object::OwningBinary<object::ObjectFile> ObjectFile,
666675
const BenchmarkKey &Key, std::optional<int> BenchmarkProcessCPU) const {
676+
bool DryRun =
677+
BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::DryRunMeasure;
678+
667679
switch (ExecutionMode) {
668680
case ExecutionModeE::InProcess: {
669681
if (BenchmarkProcessCPU.has_value())
670682
return make_error<Failure>("The inprocess execution mode does not "
671683
"support benchmark core pinning.");
672684

673685
auto InProcessExecutorOrErr = InProcessFunctionExecutorImpl::create(
674-
State, std::move(ObjectFile), Scratch.get(), BenchmarkProcessCPU);
686+
State, std::move(ObjectFile), Scratch.get(), BenchmarkProcessCPU,
687+
DryRun);
675688
if (!InProcessExecutorOrErr)
676689
return InProcessExecutorOrErr.takeError();
677690

678691
return std::move(*InProcessExecutorOrErr);
679692
}
680693
case ExecutionModeE::SubProcess: {
681694
#ifdef __linux__
695+
if (DryRun)
696+
return make_error<Failure>("The subprocess execution mode cannot "
697+
"dry-run measurement at this moment.");
698+
682699
auto SubProcessExecutorOrErr = SubProcessFunctionExecutorImpl::create(
683700
State, std::move(ObjectFile), Key, BenchmarkProcessCPU);
684701
if (!SubProcessExecutorOrErr)

llvm/tools/llvm-exegesis/lib/Target.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ ExegesisTarget::createBenchmarkRunner(
9898
return nullptr;
9999
case Benchmark::Latency:
100100
case Benchmark::InverseThroughput:
101-
if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure &&
101+
if (BenchmarkPhaseSelector >= BenchmarkPhaseSelectorE::Measure &&
102102
!PfmCounters.CycleCounter) {
103103
const char *ModeName = Mode == Benchmark::Latency
104104
? "latency"
@@ -116,7 +116,7 @@ ExegesisTarget::createBenchmarkRunner(
116116
State, Mode, BenchmarkPhaseSelector, ResultAggMode, ExecutionMode,
117117
ValidationCounters, BenchmarkRepeatCount);
118118
case Benchmark::Uops:
119-
if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure &&
119+
if (BenchmarkPhaseSelector >= BenchmarkPhaseSelectorE::Measure &&
120120
!PfmCounters.UopsCounter && !PfmCounters.IssueCounters)
121121
return make_error<Failure>(
122122
"can't run 'uops' mode, sched model does not define uops or issue "

llvm/tools/llvm-exegesis/llvm-exegesis.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,10 @@ static cl::opt<BenchmarkPhaseSelectorE> BenchmarkPhaseSelector(
132132
clEnumValN(
133133
BenchmarkPhaseSelectorE::Measure, "measure",
134134
"Same as prepare-measured-code, but also runs the measurement "
135-
"(default)")),
135+
"(default)"),
136+
clEnumValN(
137+
BenchmarkPhaseSelectorE::DryRunMeasure, "dry-run-measurement",
138+
"Same as measure, but does not actually execute the snippet")),
136139
cl::init(BenchmarkPhaseSelectorE::Measure));
137140

138141
static cl::opt<bool>
@@ -476,7 +479,7 @@ static void runBenchmarkConfigurations(
476479
}
477480

478481
void benchmarkMain() {
479-
if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure &&
482+
if (BenchmarkPhaseSelector >= BenchmarkPhaseSelectorE::Measure &&
480483
!UseDummyPerfCounters) {
481484
#ifndef HAVE_LIBPFM
482485
ExitWithError(
@@ -501,7 +504,7 @@ void benchmarkMain() {
501504

502505
// Preliminary check to ensure features needed for requested
503506
// benchmark mode are present on target CPU and/or OS.
504-
if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure)
507+
if (BenchmarkPhaseSelector >= BenchmarkPhaseSelectorE::Measure)
505508
ExitOnErr(State.getExegesisTarget().checkFeatureSupport());
506509

507510
if (ExecutionMode == BenchmarkRunner::ExecutionModeE::SubProcess &&

0 commit comments

Comments
 (0)