惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
有赞技术团队
有赞技术团队
J
Java Code Geeks
H
Hackread – Cybersecurity News, Data Breaches, AI and More
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hugging Face - Blog
Hugging Face - Blog
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Check Point Blog
博客园 - 【当耐特】
The GitHub Blog
The GitHub Blog
Recent Announcements
Recent Announcements
The Cloudflare Blog
Microsoft Azure Blog
Microsoft Azure Blog
腾讯CDC
Vercel News
Vercel News
IT之家
IT之家
MyScale Blog
MyScale Blog
博客园_首页
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
罗磊的独立博客

又见苍岚

COLMAP PatchMatch Stereo 算法详解 事件驱动的状态机框架:从理论到工程实践 Git 在国内网络环境下无法 Push 的排查与修复 —— 配置 Clash 代理 分段五次多项式插值原理详解 路径插值方法深度对比研究 Claude Code 使用指南 OpenClaw 记忆管理与技能创建指南 CBS(Conflict-Based Search)算法详解 A* 算法及其变种详解 OpenClaw 配置多 Agents Windows Powershell 无法加载文件,因为在此系统上禁止运行脚本问题的解决方案 MaxClaw 安装流程 大模型 AI 名词介绍 AList 网盘聚合工具简介 Protobuf 简介与测试 Claude Code 简介以及 GLM 4.7 模型接入 Github 歌词下载工具 163MusicLyrics Python __getattr__ 懒加载 Python TypedDict 机器人仿真平台 Gazebo 安装记录 机器人仿真平台 Gazebo 简介 多机器人路径规划问题(Multi-Agent Path Finding, MAPF)简介 Python exifread 读取修改过的 jpeg 信息错误问题修复 3D 坐标系变换的理解 3D 旋转矩阵基本概念 MongoDB Compass 介绍 Python 环境管理工具 uv Flutter 开发指南 Snipaste 安装下载与黑屏问题解决方案 全局路径规划算法记录
C++ 为进程、线程分配 CPU 资源
Yiwei Zhang · 2023-03-17 · via 又见苍岚

将不同任务分配至不同计算资源有利于资源合理分配,降低冲突,如果使用得当还可能提升系统性能,本文记录 C++ 实现 CPU 资源分配的方法。

简介

一个程序,当运算很充分(IO等操作很少)时,指定到单独一个CPU上运行会比不指定CPU运行时快。这中间主要有两个原因:

  1. CPU切换时损耗的性能。
  2. Intel的自动降频技术和windows的机制冲突:windows有一个功能是平衡负载,可以将一个线程在不同时间分配到不同CPU,从而使得每一个CPU不“过累”。然而,Inter又有一个技术叫做SpeedStep,当一个CPU没有满负荷运行时自动降频从而达到节能减排的目的。这两个功能实际是冲突的:一个程序被分配到多个CPU协同工作->每个CPU都不是满载->每个CPU都会降频->windows发现每个CPU性能都降低了,因此程序执行速度也降低了。

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

而在 C++ 编程中可以实现任务的 CPU 分配。

C++ 实现 CPU 分配

进程分配 CPU 资源

核心函数为 setProcessAffinityMask, 为指定进程的线程设置处理器关联掩码,官方文档

语法
1
2
3
4
BOOL SetProcessAffinityMask(
[in] HANDLE hProcess,
[in] DWORD_PTR dwProcessAffinityMask
);
参数
1
[in] hProcess

要设置其关联掩码的进程句柄。 此句柄必须具有 PROCESS_SET_INFORMATION 访问权限。 有关详细信息,请参阅 进程安全性和访问权限

1
[in] dwProcessAffinityMask

进程的 CPU 关联掩码。

在具有 64 个以上的处理器的系统上,关联掩码必须在单个 处理器组中指定处理器

关联掩码

关联掩码 表示的就是对应 CPU 逻辑核,此处以我的本机为例:

  • 处理器型号: i7-8700 6核 12线程,12个逻辑核

也就是有 12 个 CPU 可以用于分配,掩码用一个二进制比特位表示一个 CPU 是否启用,1为启用,0为禁用

即如果想配置第 $n$(0-11)个CPU,那么就在掩码的第 $n+1$ 个比特位置 1,即加上 $2^n$

  • 我当前想使用第 0,3, 6,9 块 CPU 工作
  • 配置掩码:$Mask = 2^0 + 2^3 + 2^6 + 2^9$

示例代码:

1
2
3
DWORD dwProcessAffinityMask = 1 + 8 + 64 + 512;
int res = SetProcessAffinityMask(GetCurrentProcess(), dwProcessAffinityMask);
// do something

可以看到进程确实控制我安排的目标 CPU 在努力工作,表示分配成功。

返回值

如果该函数成功,则返回值为非零值。

如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

如果进程相关性掩码请求未在系统中配置的处理器,则最后一个错误代码 ERROR_INVALID_PARAMETER

在具有 64 个以上的处理器的系统上,如果调用进程包含多个处理器组中的线程,则最后一个错误代码 ERROR_INVALID_PARAMETER

注解

进程相关性掩码是位向量,其中每个位表示允许进程线程运行的逻辑处理器。 进程相关性掩码的值必须是 GetProcessAffinityMask 函数获取的系统相关性掩码值的子集。 仅允许在配置为系统的处理器上运行进程。 因此,当系统关联掩码为该处理器指定 0 位时,进程相关性掩码不能为处理器指定 1 位。

进程相关性由任何子进程或新实例化本地进程继承。

请勿在 DLL 中调用 SetProcessAffinityMask ,该 DLL 可能由你自己的进程调用。

在具有 64 个以上的处理器的系统上, SetProcessAffinityMask 函数可用于仅针对单个 处理器组中具有线程的进程设置进程相关性掩码。

使用 SetThreadAffinityMask 函数为多个组中的各个线程设置相关性掩码。 这实际上改变了进程的组分配。

获取进程分配掩码

核心函数为 getProcessAffinityMask,检索指定进程的进程关联掩码和系统的系统相关性掩码。

语法
1
2
3
4
5
BOOL GetProcessAffinityMask(
[in] HANDLE hProcess,
[out] PDWORD_PTR lpProcessAffinityMask,
[out] PDWORD_PTR lpSystemAffinityMask
);
参数
1
[in] hProcess

需要关联掩码的进程句柄。

此句柄必须具有 PROCESS_QUERY_INFORMATIONPROCESS_QUERY_LIMITED_INFORMATION 访问权限。 有关详细信息,请参阅 进程安全性和访问权限

Windows Server 2003 和 Windows XP: 句柄必须具有 PROCESS_QUERY_INFORMATION 访问权限。

1
[out] lpProcessAffinityMask

指向接收指定进程的关联掩码的变量的指针。

1
[out] lpSystemAffinityMask

指向接收系统的关联掩码的变量的指针。

返回值

如果函数成功,则返回值为非零值,并且函数将 lpProcessAffinityMasklpSystemAffinityMask 指向的变量设置为适当的关联掩码。

在具有 64 个以上的处理器的系统上,如果调用进程的线程位于单个处理器组中,该函数会将 lpProcessAffinityMasklpSystemAffinityMask 指向的变量设置为进程关联掩码以及该组的活动逻辑处理器的处理器掩码。 如果调用进程包含多个组中的线程,则函数将返回这两个关联掩码的零。

如果函数失败,则返回值为零,并且 lpProcessAffinityMasklpSystemAffinityMask 指向的变量的值是未定义的。 要获得更多的错误信息,请调用 GetLastError

线程分配 CPU 资源

核心函数为 setThreadAffinityMask , 设置指定线程的处理器相关性掩码,官方文档

语法

C++复制

1
2
3
4
DWORD_PTR SetThreadAffinityMask(
[in] HANDLE hThread,
[in] DWORD_PTR dwThreadAffinityMask
);
参数
1
[in] hThread

要设置关联掩码的线程的句柄。

此句柄必须具有 THREAD_SET_INFORMATIONTHREAD_SET_LIMITED_INFORMATION 访问权限和 THREAD_QUERY_INFORMATIONTHREAD_QUERY_LIMITED_INFORMATION 访问权限。 有关详细信息,请参阅 线程安全和访问权限

Windows Server 2003 和 Windows XP: 句柄必须具有 THREAD_SET_INFORMATIONTHREAD_QUERY_INFORMATION 访问权限。

1
[in] dwThreadAffinityMask

线程的关联掩码。

在具有 64 个以上的处理器的系统上,地缘掩码必须在线程的当前 处理器组中指定处理器

返回值

如果函数成功,则返回值为线程的上一个关联掩码。

如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。

如果线程相关性掩码请求未为进程关联掩码选择的处理器,则最后一个错误代码 ERROR_INVALID_PARAMETER

注解

线程关联掩码是一个位向量,其中每个位表示允许线程在其中运行的逻辑处理器。 线程关联掩码必须是线程包含进程的进程相关性掩码的子集。 线程只能在其进程可以运行的处理器上运行。 因此,当进程相关性掩码为该处理器指定 0 位时,线程关联掩码不能为处理器指定 1 位。

为进程或线程设置关联掩码可能会导致线程接收的处理器时间较少,因为系统被限制在特定处理器上运行线程。 在大多数情况下,最好让系统选择可用的处理器。

如果新的线程关联掩码未指定当前正在运行该线程的处理器,则会在允许的处理器之一上重新计划该线程。

参考资料

文章链接:
https://www.zywvvd.com/notes/coding/cpp/cpp-cpu-task/cpp-cpu-task/