受欢迎的博客标签

多核CPU并行编程:.Visual Studio c++ SetThreadAffinityMask windows下绑定线程(进程)到指定的CPU核心

Published

windows有一个功能是平衡负载,可以将一个线程在不同时间分配到不同CPU,从而使得每一个CPU不“过累”。然而,Inter又有一个技术叫做SpeedStep,当一个CPU没有满负荷运行时自动降频从而达到节能减排的目的。

这两个功能实际是冲突的:一个程序被分配到多个CPU协同工作->每个CPU都不是满载->每个CPU都会降频->windows发现每个CPU性能都降低了,因此程序执行速度也降低了。

一个程序指定到单独一个CPU上运行会比不指定CPU运行时快。这中间主要有两个原因:
1)CPU切换时损耗的性能。
2)Intel的自动降频技术和windows的机制冲突

 

因此,将线程(进程)绑定到指定CPU核心,从而不让windows自作主张帮我们分散任务,从而提高单线程效率是很有必要的。

有两种方法实现绑定进程到指定CPU:
1)手工调节:

You can also restrict a process using the Task Manager
(right click on a process).

在资源管理器的进程里面,设置相关性,可以设置进程到某个或者某些指定的CPU核心。
 
2)用SetProcessAffinityMask代码自动调节:

On Windows there's the API function SetThreadAffinityMask.
With it you can restrict the execution of a thread to
one or more CPU cores. Related is SetProcessAffinityMask.
在Windows系统中,我们使用SetProcessAffinityMask设置线程在哪个处理器上运行。

SetThreadAffinityMask  
The SetThreadAffinityMask function sets a processor affinity mask for the specified thread.  
  
DWORD_PTR SetThreadAffinityMask(  
  HANDLE hThread,  
  DWORD_PTR dwThreadAffinityMask  
);  

If the function succeeds, the return value is the thread's previous affinity mask.

If the function fails, the return value is zero. To get extended error information, callGetLastError.

用法:

把当前线程限制在CPU0(第一个processor)上运行:

SetThreadAffinityMask(GetCurrentThread(), 1);//第0位是1 0001

要把当前线程限制在CPU1,CPU2这两个上运行:

SetThreadAffinityMask(GetCurrentThread(), 0x00000003);//3 = 0 0 1 1,设置第一、二个核

 

 

若要将3个线程限制到CPU1、2和3上去运行,可以这样操作:

//Thread 0 can only run on CPU 0.  
  
SetThreadAffinityMask(hThread0, 0x00000001); //第0位是1  
  
//Threads 1, 2, 3 run on CPUs 1, 2, 3.//第1 2 3位是1  
  
SetThreadAffinityMask(hThread1, 0x00000002);  
  
SetThreadAffinityMask(hThread2, 0x00000003);  
  
SetThreadAffinityMask(hThread3, 0x00000004);

 HANDLE thread = GetCurrentThread();
    SetThreadAffinityMask(GetCurrentThread(), 1);  

该函数中的hThread参数为线程句柄用于指明要限制哪个线程, dwThreadAffinityMask参数为一个mask,用于指明该线程能够在哪个CPU上运行。

The second parameter(dwThreadAffinityMask) to SetThreadAffinityMask() is a bit vector. Each bit corresponds to a logical processor: a CPU core or a hyper-thread. If a bit in the second parameter is set to 1, the thread is allowed to run on the corresponding core.

比如:

双核处理器

cpu0  cpu1

  x       x

6核心12线程

cpu0  cpu1 .... cpu11

x        x       ....   x

想让线程在哪个核心cpuX(或超线程)上运行,把x设为1,其余设置为0,即得到一串儿进制,计算出16进制即可。

注意事项:

dwThreadAffinityMask必须是进程的亲缘性屏蔽的相应子集。返回值是线程的前一个亲缘性屏蔽。例如,可能有一个包含4个线程的进程,它们在拥有4个CPU的计算机上运行。如果这些线程中的一个线程正在执行非常重要的操作,而你想增加某个CPU始终可供它使用的可能性,为此你对其他3个线程进行了限制,使它们不能在CPU0上运行,而只能在CPU1、2和3上运行。因此,若要将3个线程限制到CPU1、2和3上去运行,可以这样操作:

//线程0只能在cpu 0上运行
SetThreadAffinityMask(hThread0,0x00000001);
//线程1,2,3只能在cpu 1,2,3上运行
SetThreadAffinityMask(hThread1,0x0000000E);
SetThreadAffinityMask(hThread2,0x0000000E);
SetThreadAffinityMask(hThread3,0x0000000E);

环境:i3-4130(双核心四线程)

SetThreadAffinityMask(GetCurrentThread(), 0x00000001);//1 = 0 0 0 1,设置第一个核
SetThreadAffinityMask(GetCurrentThread(), 0x00000002);//2 = 0 0 1 0,设置第二个核
SetThreadAffinityMask(GetCurrentThread(), 0x00000004);//4 = 0 1 0 0,设置第三个核
SetThreadAffinityMask(GetCurrentThread(), 0x00000008);//8 = 1 0 0 0,设置第四个核
SetThreadAffinityMask(GetCurrentThread(), 0x00000003);//3 = 0 0 1 1,设置第一、二个核
SetThreadAffinityMask(GetCurrentThread(), 0x0000000F);//15 = 1 1 1 1,设置第一、二、三、四个核

要知道当前线程的句柄,通过函数:GetCurrentThread()得到。

在创建多线程的时候,也同样可以得到创建的线程的句柄。
第二个参数为mask,可取值为0~2^31(32位)和0~2^63(64位),每一位代表每一个CPU是否使用。
比如,你要指定进程到第0个CPU上,则mask=0×01
第1个CPU:mask=0×02
第2个CPU:mask=0×04 (注意不是0×03)
第3个CPU:mask=0×08
以此类推。
如果要指定多个CPU:
比如第0、1个:mask=0×03
第1、2个:mask=0×06
以此类推。
如果CPU个数不足,则会进行取模操作。比如一共4个CPU,则mask=0×0010则和0×01一样。
这种方法的好处是多线程时不用每次都手动选择CPU,缺点是万一选到的CPU负载很高,那么程序执行速度就慢了

Proper Usage of SetThreadAffinityMask

There are 12 cores, and 12 threads running..I want to bind 1 thread to each core.

You could write code like below. GetThreadHandle(i) is the function that get the handle of each thread.

int core = 12;
for(int i=0; i<core; i++)  
     SetThreadAffinityMask(GetThreadHandle(i), 1<<i);

The bitmask is typically 64 bit. A more portable solution that avoids arithmetic overflow, for cases where there are more than 32 processors would be:

int core = 12;
auto mask = (static_cast<DWORD_PTR>(1) << core);//core number starts from 0
auto ret = SetThreadAffinityMask(GetCurrentThread(), mask);

 

让用户来决定Windows任务管理器(Task Manager)的CPU占用率

https://blog.csdn.net/wesweeky/article/details/6402564

https://www.cnblogs.com/flyinghearts/archive/2011/03/22/1991965.html

从开源项目中提取的最受好评的SetThreadAffinityMask现实C++ (Cpp)示例

https://cpp.hotexamples.com/zh/examples/-/-/SetThreadAffinityMask/cpp-setthreadaffinitymask-function-examples.html