Query thread (not process) processor affinity?
Asked Answered
D

4

9

On Windows, you can call SetProcessAffinityMask for a process and SetThreadAffinityMask for a thread. However, Windows only appears to expose GetProcessAffinityMask and not a similar API for individual threads of a process.

I have a multi-threaded program that binds individual threads to processors at run-time. As I run it, I'd like to (externally) query which threads are running on which processors to make sure it's working correctly. I've written a small command-line utility to do this. But I can't seem to find a means of finding which processor(s) or core(s) an individual thread is bound to.

This apparently must be possible; I've seen descriptions online of the adplus debugging utility being able to show pstack-like output to show thread affinity. And Process Explorer shows a Threads tab on multi-processor machines that shows the "Ideal Processor" of a thread.

Does anyone know how to query this piece of information?

Drag answered 6/7, 2011 at 19:30 Comment(0)
F
10

You can do it with two calls to SetThreadAffinityMask. This function returns the original affinity mask for the passed thread handle.

So... do one call with a mask that sets affinity to one CPU, and then do a second call to restore the original mask.

Here is complete C/C++ source code including error checking:

DWORD GetThreadAffinityMask(HANDLE thread)
{
    DWORD mask = 1;
    DWORD old = 0;

    // try every CPU one by one until one works or none are left
    while(mask)
    {
        old = SetThreadAffinityMask(thread, mask);
        if(old)
        {   // this one worked
            SetThreadAffinityMask(thread, old); // restore original
            return old;
        }
        else
        {
            if(GetLastError() != ERROR_INVALID_PARAMETER)
                return 0; // fatal error, might as well throw an exception
        }
        mask <<= 1;
    }

    return 0;
}

This code probes one CPU at a time, until setting affinity works (in this case we now know the original mask!) or until the initial 1 has been shifted out of the DWORD. If a CPU is asked that is not available, the function fails with ERROR_INVALID_PARAMETER, and we just try the next one. Usually the first CPU will just work, so it's reasonably efficient.

If the function fails with anything other than ERROR_INVALID_PARAMETER, it means that we either don't have sufficient access rights to the handle, or the system is having some real problems because it can't fulfill our request. Therefore it doesn't make sense to continue in this case.

Fatshan answered 6/7, 2011 at 19:35 Comment(4)
Damon, thanks!--that works very nicely. One follow-up question: if this was integrated into a tasklist-style utility, how does this affect process performance by potentially bit-bashing the affinity mask of every thread on every running process?Drag
The "normal" case is that a thread has affinity for all CPUs, including the first one. It is really unusual that this is not the case. Therefore, you can usually expect no more than 2 calls to SetThreadAffinityMask. Also, I would expect SetThreadAffinityMask to be rather low overhead, since it can more or less directly set the information via the TEB, which does not even require a context switch. So the performance should be somewhat similar to e.g. the GetLastError function. I would not expect any noticeable impact by calling it a hundred times (though only profiling can tell for sure!).Fatshan
Still... do you really need to get the affinity of all threads on the system at the same time? Most tasklist-style programs only query the affinity for one thread at a time, if you especially ask for it. Also, in case it is really an issue, you can still query the affinities of all threads in a worker thread that queries one, and calls SwitchToThread or Sleep(0), then queries the next... and sends a message to the GUI thread for every update. That will very rapidly fill the whole list and have zero impact.Fatshan
A way to do it without a loop is to first GetProcessAffinityMask(), then SetThreadAffinityMask() to process' mask and then revert it back with another SetThreadAffinityMask() call.Haugh
A
7

Call NtQueryInformationThread, with ThreadBasicInformation:

typedef struct _THREAD_BASIC_INFORMATION
{
    NTSTATUS ExitStatus;
    PTEB TebBaseAddress;
    CLIENT_ID ClientId;
    ULONG_PTR AffinityMask;
    KPRIORITY Priority;
    LONG BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;

AFAIK there's no documented way of getting the thread affinity.

Aneto answered 8/7, 2011 at 12:13 Comment(0)
M
0

On Windows 7 and above they have GetThreadGroupAffinity

GROUP_AFFINITY affinity;

// Requires: _WIN32_WINNT >= 0x0601
if (!GetThreadGroupAffinity(GetCurrentThread(), &affinity))
{
  auto const code = GetLastError();

  throw std::system_error(
    std::error_code{ static_cast<int>(code), std::system_category()},
    "GetThreadGroupAffinity");
}

return affinity.Mask

However:

Starting with Windows 11 and Windows Server 2022, on a system with more than 64 processors, process and thread affinities span all processors in the system, across all processor groups, by default. The GetThreadGroupAffinity function retrieves the group affinity over the thread's primary group.

Magnetics answered 11/9 at 21:25 Comment(0)
B
-1

a faster way is to call GetCurrentProcessorNumber see msdn which will return the number of the processor the current thread was running on during the call to this function.

c# code :

/// <summary>
/// Retrieves the number of the processor the current thread was running on <para/>
/// during the call to this function.
/// </summary>
/// <returns>The function returns the current processor number.</returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int GetCurrentProcessorNumber();
Brobdingnagian answered 30/4, 2015 at 15:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.