Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Core Affinity Something that We Can Safely Do in .NET?

I have looked here in StackOverflow for information implementing Core Affinity for a Thread,
in .NET.

Some answers say that .NET does not support it for its own (managed) threads,
and only supports it for the non-managed threads running on the operating system.

On the other hand, other answers, mention the following properties:
- ProcessThread.IdealProcessor link
- ProcessThread.ProcessorAffinity link

As can be seen, those 2 properties are not properties of the Thread class, but of the ProcessThread class.

So I would like to ask:
If someone is creating a .NET application,
and wants to set the Core affinity for the Threads of his application,
is it safe and supported to do that, on the .NET Managed Threads?

(If yes, then I wonder why those 2 properties are exposed on the ProcessThread class
and not on the Thread class?)

PS: I am using .NET Framework v3.5 and v2.0,
and not the newer versions of the Framework.

like image 226
spaceman Avatar asked Dec 13 '22 16:12

spaceman


2 Answers

This is a more detailed answer that some people may find it useful.

If you want to do that exclusively using APIs of an existing .NET Standard, then the answer is indeed "no", and I'll explain why. I'll also discuss how to achieve that using a set of .NET Standard 2.0 APIs and either a single OS API or a single .NET Core 2.1 API.

It's true that, in general, a fixed one-to-one mapping between managed threads and native threads is not guaranteed throughout the lifetime of a managed application. This is based on the CLI standard Section I.12.3.1:

The CLI manages multiple concurrent threads of control (not necessarily the same as the threads provided by a host operating system), multiple managed heaps, and a shared memory address space.

One thing that is not stated very clearly is whether the collection of native threads that are used to run managed threads are all contained within the same process or multiple processes. But there are statements in the CLI standard and the .NET documentation that indicate that the whole managed application lives within a single OS process. Also I'm not aware of any implementation that schedules managed threads on multiple OS processes.

Let's first consider a simple case where there is only one managed thread and/or only one native thread. This case can be easily handled by setting the affinity of the whole process using the Process.ProcessorAffinity property. Irrespective of how that single managed thread is mapped to multiple native threads or that single native thread is mapped to multiple manged threads, there can only be a single affinity value for the whole managed application.

Otherwise, there can be multiple affinities. A native thread of type ProcessThread offers the write-only property ProcessorAffinity. The managed thread type Thread offers no such API. However, it offers the BeginThreadAffinity static method that allows the current managed thread to fix its mapping to whatever native thread it's currently mapped to until the managed thread calls EndThreadAffinity. Note that BeginThreadAffinity is not a hint to the runtime. Either an exception is thrown or it returns successfully with a fixed mapping for the current managed thread. Now if we could get the current native thread, we can just change its processor affinity using ProcessThread.ProcessorAffinity. Unfortunately, in contrast to getting the current managed thread, there is no standard managed API that returns a reference to or the identifier of the current native thread. As you can see, we can achieve a fixed mapping using only .NET Standard 2.0 APIs, but there is no way to figure out which native thread is mapped to which managed thread. I don't think there is a good technical reason why not to have such an API.

One way to proceed is to call some OS-dependent API to get the ID of the current native thread. On Windows, this is GetCurrentThreadId from kernel32.dll. The current managed thread can call this API to get the ID of the current native thread. Then, a reference to the corresponding ProcessThread object can be obtained by iterating over the native threads using Process.GetCurrentProcess().Threads and finding the one with the matching ID. After that, ProcessThread.ProcessorAffinity can be used to effectively set the affinity of the current managed thread. Note that since ProcessThread.ProcessorAffinity is a write-only property, there is no .NET Standard 2.0 API that enables you to restore the old affinity. I don't know why it's write-only.

Another, much more complicated, way is by using Thread.GetCurrentProcessorId, which currently exists only in .NET Core 2.1 (latest version). You can set the affinity of each native thread to a specific processor and check which managed thread is currently running on that processor. Eventually, you can determine which managed thread is mapped to which native thread.

Although it's possible to achieve a fixed mapping as discussed above, it seems to me that BeginThreadAffinity does not guarantee that each managed thread is mapped to a unique native thread. So a one-to-one mapping cannot be achieved in a standard-compliant way.

In .NET Core, ProcessThread.ProcessorAffinity is only implemented on Windows. On other OSs, it throws a PlatformNotSupportedException.

like image 187
Hadi Brais Avatar answered Dec 16 '22 06:12

Hadi Brais


No.

.NET threads do not map 1:1 to operating system threads. Thread affinity is for operating system threads. Since the runtime can switch .NET threads between OS threads at will, setting processor affinity will do nothing at best, and waste performance in a typical multi-threaded scenario.

Note that ProcessThread simply contains information about running operating system thread in a process. It's just what you get when you ask what threads a process has - it's part of the OS, not .NET. In contrast, Thread is about your threads, and only the managed ones.

like image 23
Luaan Avatar answered Dec 16 '22 04:12

Luaan