@@ -98,7 +98,8 @@ class InProcessFunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
98
98
public:
99
99
static Expected<std::unique_ptr<InProcessFunctionExecutorImpl>>
100
100
create (const LLVMState &State, object::OwningBinary<object::ObjectFile> Obj,
101
- BenchmarkRunner::ScratchSpace *Scratch) {
101
+ BenchmarkRunner::ScratchSpace *Scratch,
102
+ std::optional<int > BenchmarkProcessCPU) {
102
103
Expected<ExecutableFunction> EF =
103
104
ExecutableFunction::create (State.createTargetMachine (), std::move (Obj));
104
105
@@ -190,27 +191,31 @@ class SubProcessFunctionExecutorImpl
190
191
public:
191
192
static Expected<std::unique_ptr<SubProcessFunctionExecutorImpl>>
192
193
create (const LLVMState &State, object::OwningBinary<object::ObjectFile> Obj,
193
- const BenchmarkKey &Key) {
194
+ const BenchmarkKey &Key, std::optional< int > BenchmarkProcessCPU ) {
194
195
Expected<ExecutableFunction> EF =
195
196
ExecutableFunction::create (State.createTargetMachine (), std::move (Obj));
196
197
if (!EF)
197
198
return EF.takeError ();
198
199
199
200
return std::unique_ptr<SubProcessFunctionExecutorImpl>(
200
- new SubProcessFunctionExecutorImpl (State, std::move (*EF), Key));
201
+ new SubProcessFunctionExecutorImpl (State, std::move (*EF), Key,
202
+ BenchmarkProcessCPU));
201
203
}
202
204
203
205
private:
204
206
SubProcessFunctionExecutorImpl (const LLVMState &State,
205
207
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) {}
208
212
209
213
enum ChildProcessExitCodeE {
210
214
CounterFDReadFailed = 1 ,
211
215
RSeqDisableFailed,
212
216
FunctionDataMappingFailed,
213
- AuxiliaryMemorySetupFailed
217
+ AuxiliaryMemorySetupFailed,
218
+ SetCPUAffinityFailed
214
219
};
215
220
216
221
StringRef childProcessExitCodeToString (int ExitCode) const {
@@ -223,6 +228,8 @@ class SubProcessFunctionExecutorImpl
223
228
return " Failed to map memory for assembled snippet" ;
224
229
case ChildProcessExitCodeE::AuxiliaryMemorySetupFailed:
225
230
return " Failed to setup auxiliary memory" ;
231
+ case ChildProcessExitCodeE::SetCPUAffinityFailed:
232
+ return " Failed to set CPU affinity of the benchmarking process" ;
226
233
default :
227
234
return " Child process returned with unknown exit code" ;
228
235
}
@@ -384,6 +391,36 @@ class SubProcessFunctionExecutorImpl
384
391
return make_error<SnippetSignal>(ChildSignalInfo.si_signo );
385
392
}
386
393
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
+
387
424
Error createSubProcessAndRunBenchmark (
388
425
StringRef CounterName, SmallVectorImpl<int64_t > &CounterValues,
389
426
ArrayRef<const char *> ValidationCounters,
@@ -416,6 +453,10 @@ class SubProcessFunctionExecutorImpl
416
453
}
417
454
418
455
if (ParentOrChildPID == 0 ) {
456
+ if (BenchmarkProcessCPU.has_value ()) {
457
+ setCPUAffinityIfRequested (*BenchmarkProcessCPU);
458
+ }
459
+
419
460
// We are in the child process, close the write end of the pipe.
420
461
close (PipeFiles[1 ]);
421
462
// Unregister handlers, signal handling is now handled through ptrace in
@@ -538,6 +579,7 @@ class SubProcessFunctionExecutorImpl
538
579
const LLVMState &State;
539
580
const ExecutableFunction Function;
540
581
const BenchmarkKey &Key;
582
+ const std::optional<int > BenchmarkProcessCPU;
541
583
};
542
584
#endif // __linux__
543
585
} // namespace
@@ -615,11 +657,15 @@ BenchmarkRunner::getRunnableConfiguration(
615
657
Expected<std::unique_ptr<BenchmarkRunner::FunctionExecutor>>
616
658
BenchmarkRunner::createFunctionExecutor (
617
659
object::OwningBinary<object::ObjectFile> ObjectFile,
618
- const BenchmarkKey &Key) const {
660
+ const BenchmarkKey &Key, std::optional< int > BenchmarkProcessCPU ) const {
619
661
switch (ExecutionMode) {
620
662
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
+
621
667
auto InProcessExecutorOrErr = InProcessFunctionExecutorImpl::create (
622
- State, std::move (ObjectFile), Scratch.get ());
668
+ State, std::move (ObjectFile), Scratch.get (), BenchmarkProcessCPU );
623
669
if (!InProcessExecutorOrErr)
624
670
return InProcessExecutorOrErr.takeError ();
625
671
@@ -628,7 +674,7 @@ BenchmarkRunner::createFunctionExecutor(
628
674
case ExecutionModeE::SubProcess: {
629
675
#ifdef __linux__
630
676
auto SubProcessExecutorOrErr = SubProcessFunctionExecutorImpl::create (
631
- State, std::move (ObjectFile), Key);
677
+ State, std::move (ObjectFile), Key, BenchmarkProcessCPU );
632
678
if (!SubProcessExecutorOrErr)
633
679
return SubProcessExecutorOrErr.takeError ();
634
680
@@ -643,8 +689,8 @@ BenchmarkRunner::createFunctionExecutor(
643
689
}
644
690
645
691
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 {
648
694
Benchmark &BenchmarkResult = RC.BenchmarkResult ;
649
695
object::OwningBinary<object::ObjectFile> &ObjectFile = RC.ObjectFile ;
650
696
@@ -665,7 +711,8 @@ std::pair<Error, Benchmark> BenchmarkRunner::runConfiguration(
665
711
}
666
712
667
713
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);
669
716
if (!Executor)
670
717
return {Executor.takeError (), std::move (BenchmarkResult)};
671
718
auto NewMeasurements = runMeasurements (**Executor);
0 commit comments