Skip to content

Commit 6fc2451

Browse files
Reland "[llvm-exegesis] Add support for pinning benchmarking process to a CPU (#85168)"
This reverts commit 5e3d48a. This relands commit 9886788. This was originally causing build failures on more esoteric platforms that have different definitions of getcpu. This is only intended to be supported on x86-64 currently, so just use preprocessor definitions to special case the function.
1 parent b953914 commit 6fc2451

File tree

5 files changed

+83
-15
lines changed

5 files changed

+83
-15
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# REQUIRES: exegesis-can-measure-latency, x86_64-linux
2+
3+
# RUN: not llvm-exegesis -mtriple=x86_64-unknown-unknown -mode=latency -opcode-name=ADD64rr -execution-mode=inprocess --benchmark-process-cpu=0 2>&1 | FileCheck %s
4+
5+
# CHECK: llvm-exegesis error: The inprocess execution mode does not support benchmark core pinning.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# REQUIRES: exegesis-can-measure-latency, x86_64-linux
2+
3+
# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mode=latency -opcode-name=ADD64rr -execution-mode=subprocess | FileCheck %s
4+
5+
# CHECK: - { key: latency, value: {{[0-9.]*}}, per_snippet_value: {{[0-9.]*}}

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

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ class InProcessFunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
9898
public:
9999
static Expected<std::unique_ptr<InProcessFunctionExecutorImpl>>
100100
create(const LLVMState &State, object::OwningBinary<object::ObjectFile> Obj,
101-
BenchmarkRunner::ScratchSpace *Scratch) {
101+
BenchmarkRunner::ScratchSpace *Scratch,
102+
std::optional<int> BenchmarkProcessCPU) {
102103
Expected<ExecutableFunction> EF =
103104
ExecutableFunction::create(State.createTargetMachine(), std::move(Obj));
104105

@@ -190,27 +191,31 @@ class SubProcessFunctionExecutorImpl
190191
public:
191192
static Expected<std::unique_ptr<SubProcessFunctionExecutorImpl>>
192193
create(const LLVMState &State, object::OwningBinary<object::ObjectFile> Obj,
193-
const BenchmarkKey &Key) {
194+
const BenchmarkKey &Key, std::optional<int> BenchmarkProcessCPU) {
194195
Expected<ExecutableFunction> EF =
195196
ExecutableFunction::create(State.createTargetMachine(), std::move(Obj));
196197
if (!EF)
197198
return EF.takeError();
198199

199200
return std::unique_ptr<SubProcessFunctionExecutorImpl>(
200-
new SubProcessFunctionExecutorImpl(State, std::move(*EF), Key));
201+
new SubProcessFunctionExecutorImpl(State, std::move(*EF), Key,
202+
BenchmarkProcessCPU));
201203
}
202204

203205
private:
204206
SubProcessFunctionExecutorImpl(const LLVMState &State,
205207
ExecutableFunction Function,
206-
const BenchmarkKey &Key)
207-
: State(State), Function(std::move(Function)), Key(Key) {}
208+
const BenchmarkKey &Key,
209+
std::optional<int> BenchmarkCPU)
210+
: State(State), Function(std::move(Function)), Key(Key),
211+
BenchmarkProcessCPU(BenchmarkCPU) {}
208212

209213
enum ChildProcessExitCodeE {
210214
CounterFDReadFailed = 1,
211215
RSeqDisableFailed,
212216
FunctionDataMappingFailed,
213-
AuxiliaryMemorySetupFailed
217+
AuxiliaryMemorySetupFailed,
218+
SetCPUAffinityFailed
214219
};
215220

216221
StringRef childProcessExitCodeToString(int ExitCode) const {
@@ -223,6 +228,8 @@ class SubProcessFunctionExecutorImpl
223228
return "Failed to map memory for assembled snippet";
224229
case ChildProcessExitCodeE::AuxiliaryMemorySetupFailed:
225230
return "Failed to setup auxiliary memory";
231+
case ChildProcessExitCodeE::SetCPUAffinityFailed:
232+
return "Failed to set CPU affinity of the benchmarking process";
226233
default:
227234
return "Child process returned with unknown exit code";
228235
}
@@ -384,6 +391,36 @@ class SubProcessFunctionExecutorImpl
384391
return make_error<SnippetSignal>(ChildSignalInfo.si_signo);
385392
}
386393

394+
static void setCPUAffinityIfRequested(int CPUToUse) {
395+
// Special case this function for x86_64 for now as certain more esoteric
396+
// platforms have different definitions for some of the libc functions that
397+
// cause buildtime failures. Additionally, the subprocess executor mode (the
398+
// sole mode where this is supported) currently only supports x86_64.
399+
#if defined(__x86_64__)
400+
// Set the CPU affinity for the child process, so that we ensure that if
401+
// the user specified a CPU the process should run on, the benchmarking
402+
// process is running on that CPU.
403+
cpu_set_t CPUMask;
404+
CPU_ZERO(&CPUMask);
405+
CPU_SET(CPUToUse, &CPUMask);
406+
// TODO(boomanaiden154): Rewrite this to use LLVM primitives once they
407+
// are available.
408+
int SetAffinityReturn = sched_setaffinity(0, sizeof(CPUMask), &CPUMask);
409+
if (SetAffinityReturn == -1) {
410+
exit(ChildProcessExitCodeE::SetCPUAffinityFailed);
411+
}
412+
413+
// Check (if assertions are enabled) that we are actually running on the
414+
// CPU that was specified by the user.
415+
unsigned int CurrentCPU;
416+
assert(getcpu(&CurrentCPU, nullptr) == 0 &&
417+
"Expected getcpu call to succeed.");
418+
assert(static_cast<int>(CurrentCPU) == CPUToUse &&
419+
"Expected current CPU to equal the CPU requested by the user");
420+
#endif // defined(__x86_64__)
421+
exit(ChildProcessExitCodeE::SetCPUAffinityFailed);
422+
}
423+
387424
Error createSubProcessAndRunBenchmark(
388425
StringRef CounterName, SmallVectorImpl<int64_t> &CounterValues,
389426
ArrayRef<const char *> ValidationCounters,
@@ -416,6 +453,10 @@ class SubProcessFunctionExecutorImpl
416453
}
417454

418455
if (ParentOrChildPID == 0) {
456+
if (BenchmarkProcessCPU.has_value()) {
457+
setCPUAffinityIfRequested(*BenchmarkProcessCPU);
458+
}
459+
419460
// We are in the child process, close the write end of the pipe.
420461
close(PipeFiles[1]);
421462
// Unregister handlers, signal handling is now handled through ptrace in
@@ -538,6 +579,7 @@ class SubProcessFunctionExecutorImpl
538579
const LLVMState &State;
539580
const ExecutableFunction Function;
540581
const BenchmarkKey &Key;
582+
const std::optional<int> BenchmarkProcessCPU;
541583
};
542584
#endif // __linux__
543585
} // namespace
@@ -615,11 +657,15 @@ BenchmarkRunner::getRunnableConfiguration(
615657
Expected<std::unique_ptr<BenchmarkRunner::FunctionExecutor>>
616658
BenchmarkRunner::createFunctionExecutor(
617659
object::OwningBinary<object::ObjectFile> ObjectFile,
618-
const BenchmarkKey &Key) const {
660+
const BenchmarkKey &Key, std::optional<int> BenchmarkProcessCPU) const {
619661
switch (ExecutionMode) {
620662
case ExecutionModeE::InProcess: {
663+
if (BenchmarkProcessCPU.has_value())
664+
return make_error<Failure>("The inprocess execution mode does not "
665+
"support benchmark core pinning.");
666+
621667
auto InProcessExecutorOrErr = InProcessFunctionExecutorImpl::create(
622-
State, std::move(ObjectFile), Scratch.get());
668+
State, std::move(ObjectFile), Scratch.get(), BenchmarkProcessCPU);
623669
if (!InProcessExecutorOrErr)
624670
return InProcessExecutorOrErr.takeError();
625671

@@ -628,7 +674,7 @@ BenchmarkRunner::createFunctionExecutor(
628674
case ExecutionModeE::SubProcess: {
629675
#ifdef __linux__
630676
auto SubProcessExecutorOrErr = SubProcessFunctionExecutorImpl::create(
631-
State, std::move(ObjectFile), Key);
677+
State, std::move(ObjectFile), Key, BenchmarkProcessCPU);
632678
if (!SubProcessExecutorOrErr)
633679
return SubProcessExecutorOrErr.takeError();
634680

@@ -643,8 +689,8 @@ BenchmarkRunner::createFunctionExecutor(
643689
}
644690

645691
std::pair<Error, Benchmark> BenchmarkRunner::runConfiguration(
646-
RunnableConfiguration &&RC,
647-
const std::optional<StringRef> &DumpFile) const {
692+
RunnableConfiguration &&RC, const std::optional<StringRef> &DumpFile,
693+
std::optional<int> BenchmarkProcessCPU) const {
648694
Benchmark &BenchmarkResult = RC.BenchmarkResult;
649695
object::OwningBinary<object::ObjectFile> &ObjectFile = RC.ObjectFile;
650696

@@ -665,7 +711,8 @@ std::pair<Error, Benchmark> BenchmarkRunner::runConfiguration(
665711
}
666712

667713
Expected<std::unique_ptr<BenchmarkRunner::FunctionExecutor>> Executor =
668-
createFunctionExecutor(std::move(ObjectFile), RC.BenchmarkResult.Key);
714+
createFunctionExecutor(std::move(ObjectFile), RC.BenchmarkResult.Key,
715+
BenchmarkProcessCPU);
669716
if (!Executor)
670717
return {Executor.takeError(), std::move(BenchmarkResult)};
671718
auto NewMeasurements = runMeasurements(**Executor);

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ class BenchmarkRunner {
6868

6969
std::pair<Error, Benchmark>
7070
runConfiguration(RunnableConfiguration &&RC,
71-
const std::optional<StringRef> &DumpFile) const;
71+
const std::optional<StringRef> &DumpFile,
72+
std::optional<int> BenchmarkProcessCPU) const;
7273

7374
// Scratch space to run instructions that touch memory.
7475
struct ScratchSpace {
@@ -135,7 +136,8 @@ class BenchmarkRunner {
135136

136137
Expected<std::unique_ptr<FunctionExecutor>>
137138
createFunctionExecutor(object::OwningBinary<object::ObjectFile> Obj,
138-
const BenchmarkKey &Key) const;
139+
const BenchmarkKey &Key,
140+
std::optional<int> BenchmarkProcessCPU) const;
139141
};
140142

141143
} // namespace exegesis

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@ static cl::list<ValidationEvent> ValidationCounters(
269269
"counter to validate benchmarking assumptions"),
270270
cl::CommaSeparated, cl::cat(BenchmarkOptions), ValidationEventOptions());
271271

272+
static cl::opt<int> BenchmarkProcessCPU(
273+
"benchmark-process-cpu",
274+
cl::desc("The CPU number that the benchmarking process should executon on"),
275+
cl::cat(BenchmarkOptions), cl::init(-1));
276+
272277
static ExitOnError ExitOnErr("llvm-exegesis error: ");
273278

274279
// Helper function that logs the error(s) and exits.
@@ -418,8 +423,12 @@ static void runBenchmarkConfigurations(
418423
std::optional<StringRef> DumpFile;
419424
if (DumpObjectToDisk.getNumOccurrences())
420425
DumpFile = DumpObjectToDisk;
426+
const std::optional<int> BenchmarkCPU =
427+
BenchmarkProcessCPU == -1
428+
? std::nullopt
429+
: std::optional(BenchmarkProcessCPU.getValue());
421430
auto [Err, BenchmarkResult] =
422-
Runner.runConfiguration(std::move(RC), DumpFile);
431+
Runner.runConfiguration(std::move(RC), DumpFile, BenchmarkCPU);
423432
if (Err) {
424433
// Errors from executing the snippets are fine.
425434
// All other errors are a framework issue and should fail.

0 commit comments

Comments
 (0)