How to Programmatically Pick and Choose Which Core of a Multi-Core CPU My Thread Should Run On

Can I programmatically pick and choose which core of a multi-core CPU my thread should run on?

For linux os, sched_setaffinity is your answer. It's supported since linux kernel 2.5.8.

Name

sched_setaffinity, sched_getaffinity — set and get a process's CPU affinity mask

#define _GNU_SOURCE
#include <sched.h>

int sched_setaffinity( pid_t pid,
size_t cpusetsize,
cpu_set_t *mask);

int sched_getaffinity( pid_t pid,
size_t cpusetsize,
cpu_set_t *mask);

The affinity mask is actually a
per-thread attribute that can be
adjusted independently for each of the
threads in a thread group. The value
returned from a call to gettid(2) can
be passed in the argument pid.
Specifying pid as 0 will set the
attribute for the calling thread, and
passing the value returned from a call
to getpid(2) will set the attribute
for the main thread of the thread
group. (If you are using the POSIX
threads API, then use
pthread_setaffinity_np(3) instead of
sched_setaffinity().)

How to control which core a process runs on?

As others have mentioned, processor affinity is Operating System specific. If you want to do this outside the confines of the operating system, you're in for a lot of fun, and by that I mean pain.

That said, others have mentioned SetProcessAffinityMask for Win32. Nobody has mentioned the Linux kernel way to set processor affinity, and so I shall. You need to use the sched_setaffinity(2) system call. Here's a nice tutorial on how.

The command-line wrapper for this system call is taskset(1). e.g.

taskset -c 2,3 perf stat awk 'BEGIN{for(i=0;i<100000000;i++){}}' restricts that perf-stat of a busy-loop to running on either of core 2 or 3 (still allowing it to migrate between cores, but only between those two).

Assigning a thread to specific CPU core

Boost is meant to be a cross platform library while the question you linked to points toward a OS specific implementation which to my knowledge is the only way to do what you are asking.

Change a thread's target core

I have a library called Java Thread Affinity which allows you to assign running threads to logical thread or cores.

In your case, you need to allow one core for the OS as a minimum, and you can reserve all the remaining cores in Linux. The thread library assigns threads to these reserved cores. The reason you want reserved cpus is to prevent other threads running on that cpu. You can tell the library to assign a thread to a logical cpu, or a whole core. You can assign threads to be on the same or different core or socket as another thread.

https://github.com/peter-lawrey/Java-Thread-Affinity/wiki/Getting-started

The library currently supports Windows and Linux.

How do I get my app to run on multiple cores?

It looks like you are trying to create a separate thread for every CPU other than the "current" CPU that is running your OnClick handler. But, you never use CPU 0 in your affinity masks, because you increment a too soon. But more importantly, a thread's affinity mask must be a subset of the process's affinity mask, which specifies the CPUs the process is allowed to run on:

A thread can only run on the processors its process can run on. Therefore, the thread affinity mask cannot specify a 1 bit for a processor when the process affinity mask specifies a 0 bit for that processor.

The process affinity mask is itself a subset of the system affinity mask, which specifies which CPUs are installed.

So, the likely cause of your error is that you are calculating thread affinity masks that the OS rejects as invalid for your process.

Try something more like this instead (note: this doesn't take CPU processor groups into account, if the OS has more than 64 CPUs installed):

procedure TForm2.BtnCreateLookup5x5to3x3UsingSpeculativeExplorationClick(Sender: TObject);
var
ThreadCount, MaxThreadCount: integer;
Threads: TArray<TThread>;
i, CurrentProcessor: integer;
ProcessAffinityMask, SystemAffinityMask, AllowedThreadMask, NewThreadMask: DWORD_PTR;
Thread: TThread;
...
begin
if not GetProcessAffinityMask(GetCurrentProcess(), ProcessAffinityMask, SystemAffinityMask) then RaiseLastOSError;

// optional: up the CPUs this process can run on, if needed...
{
if not SetProcessAffinityMask(GetCurrentProcess(), SystemAffinityMask) then RaiseLastOSError;
ProcessAffinityMask := SystemAffinityMask;
}

AllowedThreadMask := DWORD_PTR(-1) and ProcessAffinityMask;
CurrentProcessor := GetCurrentProcessorNumber;

ThreadCount := 0;
MaxThreadCount := System.CpuCount;
NewThreadMask := 1;

SetLength(Threads, MaxThreadCount);
try
for i := 0 to MaxThreadCount-1 do
begin
if (i <> CurrentProcessor) and //Skip the current processor.
((AllowedThreadMask and NewThreadMask) <> 0) then // is this CPU allowed?
begin
Thread := TThread.CreateAnonymousThread(
procedure
begin
CreateLookupUsingGridSolver(...);
end
);
try
Thread.FreeOnTerminate := false;
if not SetThreadAffinityMask(Thread.Handle, NewThreadMask) then RaiseLastOSError;
Thread.Start;
except
Thread.Free;
raise;
end;
Threads[ThreadCount] := Thread;
Inc(ThreadCount);
end;
NewThreadMask := NewThreadMask shl 1;
end;
CreateLookupUsingGridSolver(...);

// Wait for all threads to finish...
// ...

finally
for i := 0 to ThreadCount-1 do
Threads[i].Free;
end;
end;

How to find the Number of CPU Cores via .NET/C#?

There are several different pieces of information relating to processors that you could get:

  1. Number of physical processors
  2. Number of cores
  3. Number of logical processors.

These can all be different; in the case of a machine with 2 dual-core hyper-threading-enabled processors, there are 2 physical processors, 4 cores, and 8 logical processors.

The number of logical processors is available through the Environment class, but the other information is only available through WMI (and you may have to install some hotfixes or service packs to get it on some systems):

Make sure to add a reference in your project to System.Management.dll
In .NET Core, this is available (for Windows only) as a NuGet package.

Physical Processors:

foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
Console.WriteLine("Number Of Physical Processors: {0} ", item["NumberOfProcessors"]);
}

Cores:

int coreCount = 0;
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
{
coreCount += int.Parse(item["NumberOfCores"].ToString());
}
Console.WriteLine("Number Of Cores: {0}", coreCount);

Logical Processors:

Console.WriteLine("Number Of Logical Processors: {0}", Environment.ProcessorCount);

OR

foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
Console.WriteLine("Number Of Logical Processors: {0}", item["NumberOfLogicalProcessors"]);
}

Processors excluded from Windows:

You can also use Windows API calls in setupapi.dll to discover processors that have been excluded from Windows (e.g. through boot settings) and aren't detectable using the above means. The code below gives the total number of logical processors (I haven't been able to figure out how to differentiate physical from logical processors) that exist, including those that have been excluded from Windows:

static void Main(string[] args)
{
int deviceCount = 0;
IntPtr deviceList = IntPtr.Zero;
// GUID for processor classid
Guid processorGuid = new Guid("{50127dc3-0f36-415e-a6cc-4cb3be910b65}");

try
{
// get a list of all processor devices
deviceList = SetupDiGetClassDevs(ref processorGuid, "ACPI", IntPtr.Zero, (int)DIGCF.PRESENT);
// attempt to process each item in the list
for (int deviceNumber = 0; ; deviceNumber++)
{
SP_DEVINFO_DATA deviceInfo = new SP_DEVINFO_DATA();
deviceInfo.cbSize = Marshal.SizeOf(deviceInfo);

// attempt to read the device info from the list, if this fails, we're at the end of the list
if (!SetupDiEnumDeviceInfo(deviceList, deviceNumber, ref deviceInfo))
{
deviceCount = deviceNumber;
break;
}
}
}
finally
{
if (deviceList != IntPtr.Zero) { SetupDiDestroyDeviceInfoList(deviceList); }
}
Console.WriteLine("Number of cores: {0}", deviceCount);
}

[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPStr)]String enumerator,
IntPtr hwndParent,
Int32 Flags);

[DllImport("setupapi.dll", SetLastError = true)]
private static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
Int32 MemberIndex,
ref SP_DEVINFO_DATA DeviceInterfaceData);

[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
public int cbSize;
public Guid ClassGuid;
public uint DevInst;
public IntPtr Reserved;
}

private enum DIGCF
{
DEFAULT = 0x1,
PRESENT = 0x2,
ALLCLASSES = 0x4,
PROFILE = 0x8,
DEVICEINTERFACE = 0x10,
}

SetProcessAffinityMask - Select more than one processor?

It is a bitmask as described in the documentation.

A process affinity mask is a bit vector in which each bit represents a logical processor on which the threads of the process are allowed to run.

  • Processor 0 is $01.
  • Processor 1 is $02.
  • Processor 2 is $04.
  • Processor 3 is $08.
  • Processor 4 is $10.

And so on. You can use logical or to combine them. So processors 0 and 1 would be $01 or $02 which equals $03.

I would use the shift operator shl to create values for specific processors. Like this:

function SingleProcessorMask(const ProcessorIndex: Integer): DWORD_PTR;
begin
//When shifting constants the compiler will force the result to be 32-bit
//if you have more than 32 processors, `Result:= 1 shl x` will return
//an incorrect result.
Result := DWORD_PTR(1) shl (ProcessorIndex);
end;

You can readily extend this to generate masks for lists of processors using logical or in a loop.

function CombinedProcessorMask(const Processors: array of Integer): DWORD_PTR;
var
i: Integer;
begin
Result := 0;
for i := low(Processors) to high(Processors) do
Result := Result or SingleProcessorMask(Processors[i]);
end;

You can test for a processor being in a bit mask like this:

function ProcessorInMask(const ProcessorMask: DWORD_PTR; 
const ProcessorIndex: Integer): Boolean;
begin
Result := (SingleProcessorMask(ProcessorIndex) and ProcessorMask)<>0;
end;

Note: I'm using DWORD_PTR because for 64 bit targets the bitmask is 64 bits wide. That nuance doesn't matter for you on XE but it's worth getting it right to make any future code porting easier.

How to get number of CPU's logical cores/threads in C#?

You can get number of logical processors through the Environment class

number of cores:

int coreCount = 0;
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
{
coreCount += int.Parse(item["NumberOfCores"].ToString());
}
Console.WriteLine("Number Of Cores: {0}", coreCount);

number of logical processors

foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
Console.WriteLine("Number Of Logical Processors: {0}", item["NumberOfLogicalProcessors"]);
}



Environment.ProcessorCount

 using System;

class Sample
{
public static void Main()
{
Console.WriteLine("The number of processors on this computer is {0}.",
Environment.ProcessorCount);
}
}

go through this link http://msdn.microsoft.com/en-us/library/system.environment.processorcount.aspx



Related Topics



Leave a reply



Submit