@@ -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,29 @@ class SubProcessFunctionExecutorImpl
384
391
return make_error<SnippetSignal>(ChildSignalInfo.si_signo );
385
392
}
386
393
394
+ static void setCPUAffinityIfRequested (int CPUToUse) {
395
+ // Set the CPU affinity for the child process, so that we ensure that if
396
+ // the user specified a CPU the process should run on, the benchmarking
397
+ // process is running on that CPU.
398
+ cpu_set_t CPUMask;
399
+ CPU_ZERO (&CPUMask);
400
+ CPU_SET (CPUToUse, &CPUMask);
401
+ // TODO(boomanaiden154): Rewrite this to use LLVM primitives once they
402
+ // are available.
403
+ int SetAffinityReturn = sched_setaffinity (0 , sizeof (CPUMask), &CPUMask);
404
+ if (SetAffinityReturn == -1 ) {
405
+ exit (ChildProcessExitCodeE::SetCPUAffinityFailed);
406
+ }
407
+
408
+ // Check (if assertions are enabled) that we are actually running on the
409
+ // CPU that was specified by the user.
410
+ unsigned int CurrentCPU;
411
+ assert (getcpu (&CurrentCPU, nullptr ) == 0 &&
412
+ " Expected getcpu call to succeed." );
413
+ assert (static_cast <int >(CurrentCPU) == CPUToUse &&
414
+ " Expected current CPU to equal the CPU requested by the user" );
415
+ }
416
+
387
417
Error createSubProcessAndRunBenchmark (
388
418
StringRef CounterName, SmallVectorImpl<int64_t > &CounterValues,
389
419
ArrayRef<const char *> ValidationCounters,
@@ -416,6 +446,10 @@ class SubProcessFunctionExecutorImpl
416
446
}
417
447
418
448
if (ParentOrChildPID == 0 ) {
449
+ if (BenchmarkProcessCPU.has_value ()) {
450
+ setCPUAffinityIfRequested (*BenchmarkProcessCPU);
451
+ }
452
+
419
453
// We are in the child process, close the write end of the pipe.
420
454
close (PipeFiles[1 ]);
421
455
// Unregister handlers, signal handling is now handled through ptrace in
@@ -538,6 +572,7 @@ class SubProcessFunctionExecutorImpl
538
572
const LLVMState &State;
539
573
const ExecutableFunction Function;
540
574
const BenchmarkKey &Key;
575
+ const std::optional<int > BenchmarkProcessCPU;
541
576
};
542
577
#endif // __linux__
543
578
} // namespace
@@ -615,11 +650,15 @@ BenchmarkRunner::getRunnableConfiguration(
615
650
Expected<std::unique_ptr<BenchmarkRunner::FunctionExecutor>>
616
651
BenchmarkRunner::createFunctionExecutor (
617
652
object::OwningBinary<object::ObjectFile> ObjectFile,
618
- const BenchmarkKey &Key) const {
653
+ const BenchmarkKey &Key, std::optional< int > BenchmarkProcessCPU ) const {
619
654
switch (ExecutionMode) {
620
655
case ExecutionModeE::InProcess: {
656
+ if (BenchmarkProcessCPU.has_value ())
657
+ return make_error<Failure>(" The inprocess execution mode does not "
658
+ " support benchmark core pinning." );
659
+
621
660
auto InProcessExecutorOrErr = InProcessFunctionExecutorImpl::create (
622
- State, std::move (ObjectFile), Scratch.get ());
661
+ State, std::move (ObjectFile), Scratch.get (), BenchmarkProcessCPU );
623
662
if (!InProcessExecutorOrErr)
624
663
return InProcessExecutorOrErr.takeError ();
625
664
@@ -628,7 +667,7 @@ BenchmarkRunner::createFunctionExecutor(
628
667
case ExecutionModeE::SubProcess: {
629
668
#ifdef __linux__
630
669
auto SubProcessExecutorOrErr = SubProcessFunctionExecutorImpl::create (
631
- State, std::move (ObjectFile), Key);
670
+ State, std::move (ObjectFile), Key, BenchmarkProcessCPU );
632
671
if (!SubProcessExecutorOrErr)
633
672
return SubProcessExecutorOrErr.takeError ();
634
673
@@ -643,8 +682,8 @@ BenchmarkRunner::createFunctionExecutor(
643
682
}
644
683
645
684
std::pair<Error, Benchmark> BenchmarkRunner::runConfiguration (
646
- RunnableConfiguration &&RC,
647
- const std::optional<StringRef> &DumpFile ) const {
685
+ RunnableConfiguration &&RC, const std::optional<StringRef> &DumpFile,
686
+ std::optional<int > BenchmarkProcessCPU ) const {
648
687
Benchmark &BenchmarkResult = RC.BenchmarkResult ;
649
688
object::OwningBinary<object::ObjectFile> &ObjectFile = RC.ObjectFile ;
650
689
@@ -665,7 +704,8 @@ std::pair<Error, Benchmark> BenchmarkRunner::runConfiguration(
665
704
}
666
705
667
706
Expected<std::unique_ptr<BenchmarkRunner::FunctionExecutor>> Executor =
668
- createFunctionExecutor (std::move (ObjectFile), RC.BenchmarkResult .Key );
707
+ createFunctionExecutor (std::move (ObjectFile), RC.BenchmarkResult .Key ,
708
+ BenchmarkProcessCPU);
669
709
if (!Executor)
670
710
return {Executor.takeError (), std::move (BenchmarkResult)};
671
711
auto NewMeasurements = runMeasurements (**Executor);
0 commit comments