线程绑定 CPU

1. 绑定目的

在 Linux 系统中,线程调度是由内核自主完成的。当系统运行在多核 CPU 上时,线程有可能在不同的 CPU 核上来回切换执行。这不利于 CPU 的缓存,缺页率较高。

以 Intel i5 CPU 为例。在多核 CPU 结构中,每个 CPU 有各自的 L1、L2 缓存,而 L3 缓存是共用的。如果一个线程在 CPU 间来回切换,各个 CPU 的缓存的命中率将会降低。而如果线程不管如何调度,都始终可以在一个 CPU 上执行,那么其数据的 L1、L2 缓存的命中率将得到显著提高。

2. 使用命令进行绑定

使用命令 taskset 可以把线程绑定为指定 CPU 上。

taskset 命令使用方法如下:

taskset -p <cpu_set> <pid>

线程的 pid 可以使用 pstree -p | grep <pthread_name> 来获取。但该方法的前提是线程必须已设置名称,否则无法使用 pstree 来查看线程。

cpu_set 为线程对应的 CPU 集,为整型类型,其数值为 1 的 bit 位对应着哪一号 CPU(从 0 起算)。例如 cpu_set 为 1(0001)时对应 1 号 CPU,为 4(0100)时对应 2 号 CPU。

当 cpu_set 有多个 bit 位为 1 时,表示系统会把线程随机调度到在这些 CPU 之一上运行。例如当线程对应的 cpu_set 为 3 即二进制 0011 时,表示该线程将被系统随机地在 0 号 CPU 或 1 号 CPU 上调度运行。

当 taskset 不使用 <cpu_set> 只使用 -p <pid> 时,表示查询该线程对应的 CPU 集情况。

以下示例将使用命令 taskset 把线程号为 1958 的线程绑定为 1 号 CPU 上:

~ # taskset -p 1958
pid 1958's current affinity mask: 3
~ # taskset -p 2 1958
pid 1958's current affinity mask: 3
pid 1958's new affinity mask: 2

3. 在程序中进行绑定

在 Linux 中,可以使用 sched_setaffinity() 改变线程对应的 CPU 集,把线程绑定到指定 CPU 上。

函数 sched_setaffinity 所属头文件 <sched.h>,函数原型如下:

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

可见,该实际上就是把线程 pid 设置为指定 CPU 集。当传参 pid 为 0 时表示把当前线程的 CPU 集设置为 mask

cpu_set_t 类型本质上为一个整型数组,Linux 提供以下 5 个宏函数操作 CPU 集(以下数据类型仅供阅读):

宏函数 功能
CPU_SET(int cpu, cpu_set_t* cpusetp) cpu 添加到 cpusetp
CPU_CLR(int cpu, cpu_set_t* cpusetp) cpucpusetp 中删除
int CPU_ISSET(int cpu, cpu_set_t* cpusetp) 判断 cpu 是否在 cpusetp 中,存在则返回 1,否则返回 0
CPU_ZERO(cpu_set_t* cpusetp) 清空 cpusetp,即把 cpusetp 的每一 bit 位清零
CPU_COUNT(cpu_set_t* cpusetp) 查询 cpusetp 的 bit 位为 1 的个数,即线程调度的 CPU 个数

其中,cpu 指的是哪一号 CPU,该编号从 0 起算。

在使用以上宏时,需先打开宏 __USE_CHU_GNU_SOURCE 告诉编译器启动以上宏函数,且这些宏声明必须在所有头文件声明之前:

#ifndef __USE_CHU
#define __USE_CHU
#endif

#define _GNU_SOURCE

#include <sched.h>

以下示例把当前线程绑定为 3 号 CPU 上:

cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(3, &mask);
sched_setaffinity(0, sizeof(cpu_set_t), &mask);
发布了60 篇原创文章 · 获赞 36 · 访问量 5867

猜你喜欢

转载自blog.csdn.net/qq_35692077/article/details/104045657