前言:
在多核CPU的机器中,进程的运行在初始状态下,未经过可以设置的情况下,会被调度到不同CPU核心执行,由于CPU的硬件结构分为L1 L2 L3缓存,如图所示
每个CPU核心都有自己的L1 L2缓存,所有CPU核心共用一个L3缓存。如果一个进程在核心间来回切换,各个核心的缓存命中率就会受到影响。相反如果进程不管如何调度,都始终可以在一个核心上执行,那么其数据的L1、L2 缓存的命中率可以显著提高。

查看进程与CPU亲和力:
进程会运行在有亲和力的CPU核心上,可能是一个,也可能是多个,默认是所有CPU核心。
命令:
Taskset
$ taskset -p 14795
pid 14795's current affinity mask: 3
$ taskset -cp 14795
pid 14795's current affinity list: 0,1
使用taskset -p
ffffff 是 24 位的掩码,转换为二进制是 111111111111111111111111,表示允许在 CPU 0 到 CPU 23 上运行(如果系统有 24 个 CPU)。
3则代表000000000000000000000011,表示允许在 CPU 0 到 CPU 1 上运行(如果系统有 2 个 CPU)。
使用taskset -cp
则直接显示进程会运行在几号到几号CPU中
pid 14795's current affinity list: 0,1 0,1就是0号到1号,共两个
pid 1347198's current affinity list: 0-23 则代表0号到23号,共24个
CPU进程绑定:
将进程绑定到CPU有几种方式:
- 外部命令:
在linux中执行taskset命令
- 内部接口:
#include <sched.h>
进程CPU绑定函数:
int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *set);
参数列表:进程pid ,CPU位集大小,CPU位集
返回值:成功: 如果函数调用成功,sched_setaffinity 返回 0。 失败: 如果函数调用失败,返回 -1,并且 errno 被设置为相应的错误码。常见的错误码包括:
cpu_set_t 是一个位集(bit set),每一位代表一个 CPU。位的值为 1 表示该 CPU 被包含在亲和性集合中,值为 0 则表示不包含。
绑定API需要通过位集中的信息进行绑定,所以引出以下位集操作:
void CPU_ZERO(cpu_set_t *set); // 初始化 cpu_set_t 集合,将所有位清零。
void CPU_SET(int cpu, cpu_set_t *set); // 将指定的 CPU 加入到 cpu_set_t 集合中。
void CPU_CLR(int cpu, cpu_set_t *set); // 从 cpu_set_t 集合中移除指定的 CPU。
int CPU_ISSET(int cpu, cpu_set_t *set); // 检查指定的 CPU 是否在集合中。
int CPU_COUNT(cpu_set_t * mask); //返回位集中CPU数
实际操作步骤:
//1.创建CPU位集 cpu_set
cpu_set_t cpu_set;
//2.初始化 cpu_set
CPU_ZERO(&cpu_set);
//3.将 CPU 0 和 CPU 1 添加到 cpu_set
CPU_SET(0, &cpu_set);
CPU_SET(1, &cpu_set);
//4.设置当前进程的 CPU 亲和性
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) == -1)
{
perror("sched_setaffinity");
exit(EXIT_FAILURE);
}
简单对比测试:
以下编写一个简单的程序,在进程中执行计算任务,并统计执行时间,对比
完整代码示例如下:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <sched.h>
#include <time.h>
#include <stdlib.h>
void compute() {
volatile double sum = 0.0;
for (long i = 0; i < 1e8; i++) {
sum += (double)i * 3.14;
}
}
int main() {
cpu_set_t cpuset;
CPU_ZERO(&cpuset); // 清空 cpuset
CPU_SET(0, &cpuset);
pid_t pid = getpid();
if (sched_setaffinity(pid, sizeof(cpu_set_t), &cpuset) == -1) {
perror("sched_setaffinity");
return EXIT_FAILURE;
}
printf("进程 %d 绑定到 CPU 0 \n", pid);
clock_t start = clock();
for(int i =0;i<100;i++)
{
compute();
}
clock_t end = clock();
double time_taken = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("计算完成,耗时: %f 秒\n", time_taken);
return 0;
}
绑定CPU前:
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
计算完成,耗时: 39.734942 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
计算完成,耗时: 40.413664 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
计算完成,耗时: 40.293440 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
计算完成,耗时: 39.155088 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
计算完成,耗时: 38.862854 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
计算完成,耗时: 39.195169 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
计算完成,耗时: 39.241059 秒
平均耗时39.6993
绑定CPU后:
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
进程 4001147 绑定到 CPU 0
计算完成,耗时: 38.651488 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
进程 4001854 绑定到 CPU 0
计算完成,耗时: 38.631618 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
进程 4002279 绑定到 CPU 0
计算完成,耗时: 38.686167 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
进程 4002718 绑定到 CPU 0
计算完成,耗时: 38.670791 秒
[root@SSA-PM-UAT-quotation CPUBIND]# ./testtime
进程 4003149 绑定到 CPU 0
计算完成,耗时: 38.713021 秒
平均耗时38.6706 秒
可以看出,绑定进程到特定CPU可以提升性能,本测试例子提升幅度约2.59%。
2万+

被折叠的 条评论
为什么被折叠?



