进程绑定固定CPU的性能提升

前言:

在多核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有几种方式:

  1. 外部命令:

在linux中执行taskset命令

  1. 内部接口:

#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%。

参考:进程绑定CPU核心以提高性能_解绑cpu0-优快云博客

<think>好的,我现在需要帮助用户了解如何在不同操作系统(Linux和Windows)上将线程绑定固定CPU核心上。首先,我得回忆一下相关的知识,并结合提供的引用内容来组织回答。 用户的问题提到了线程绑定CPU核心,以及调度策略在Linux和Windows下的区别。根据提供的引用,引用[1]讨论了Linux和Windows的调度策略和亲和性设置,引用[2]和引用[3]详细说明了Linux下的CPU亲和性配置和线程绑定,引用[4]和引用[5]补充了Linux的调度策略和时间片轮转的信息。 首先,我需要明确线程绑定CPU核心的意义,即通过设置CPU亲和性,让线程在特定的核心上运行,这样可以减少上下文切换,提高缓存利用率,优化性能。这在实时系统或需要高性能计算的场景中尤为重要。 接下来,针对Linux和Windows分别说明方法。对于Linux,用户可以使用sched_setaffinity系统调用或pthread_setaffinity_np函数,同时需要结合调度策略(如SCHED_FIFO、SCHED_RR、SCHED_OTHER)。引用[2]提到基于进程CPU配置,而引用[3]强调设置亲和性以便测试调度策略的效果。需要详细说明如何编码实现,包括示例代码和命令行的taskset工具。 对于Windows,系统提供了SetThreadAffinityMask函数,以及通过任务管理器手动设置。引用[1]提到了Windows的调度策略,但可能不如Linux灵活,需要指出这点。同时,Windows的实时优先级需要管理员权限,这点需要注意。 然后,调度策略部分,Linux的调度策略包括SCHED_NORMAL(分时)、SCHED_BATCH、SCHED_IDLE,以及实时策略SCHED_FIFO和SCHED_RR,而Windows则分为前台优先级提升、时间片轮转和动态优先级调整。需要引用[5]中关于SCHED_NORMAL和其他策略的信息,说明如何设置优先级和策略。 在回答中需要分点清晰,结构明确,结合代码示例和命令行操作。同时,根据用户要求,生成相关问题,比如性能优化、适用场景、工具使用等。 最后,检查是否有遗漏,比如权限要求、不同操作系统的限制,以及实际应用中的注意事项,确保信息准确可靠,引用正确的参考文献。</think>### 如何将线程绑定固定CPU核心(Linux/Windows) #### 一、Linux系统实现 1. **CPU亲和性设置** - **API方法**:使用`pthread_setaffinity_np`或`sched_setaffinity`函数绑定线程到特定核心。 - **示例代码**: ```c #include <pthread.h> cpu_set_t cpuset; pthread_t thread = pthread_self(); CPU_ZERO(&cpuset); CPU_SET(2, &cpuset); // 绑定到核心2 pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); ``` - **命令行工具**:`taskset`命令可设置进程或线程的亲和性,例如`taskset -c 2 ./program`[^2][^3]。 2. **调度策略配置** - **实时线程**:使用`SCHED_FIFO`(先进先出)或`SCHED_RR`(时间片轮转),需配合优先级设置。 ```c struct sched_param param; param.sched_priority = 99; pthread_setschedparam(thread, SCHED_FIFO, &param); ``` - **普通线程**:默认策略为`SCHED_NORMAL`(分时调度),通过`nice值`调整时间片分配[^5]。 #### 二、Windows系统实现 1. **CPU亲和性设置** - **API方法**:使用`SetThreadAffinityMask`函数绑定线程。 ```cpp DWORD_PTR affinity_mask = 1 << 3; // 绑定到核心3 SetThreadAffinityMask(GetCurrentThread(), affinity_mask); ``` - **任务管理器**:手动设置进程的亲和性(右键进程 → 设置相关性)[^1]。 2. **调度策略限制** - Windows不提供类似Linux的多样化实时调度策略,主要通过优先级类(如`REALTIME_PRIORITY_CLASS`)调整线程执行顺序。 - 实时优先级需管理员权限,且可能影响系统稳定性。 #### 三、关键区别与注意事项 1. **灵活性差异** - Linux支持更细粒度的调度策略(如`SCHED_DEADLINE`)和动态优先级调整。 - Windows调度策略较为固定,以时间片轮转和动态优先级为主[^1]。 2. **性能优化场景** - **高并发计算**:绑定核心可减少缓存失效,提升计算密集型任务效率[^2]。 - **实时系统**:Linux的`SCHED_FIFO`适合硬实时需求(如工业控制)。 3. **权限与安全性** - Linux需`CAP_SYS_NICE`权限设置实时策略。 - Windows需管理员权限修改高优先级线程。 §§ 1. 如何评估线程绑定CPU核心后的性能提升效果? 2. Linux中`SCHED_FIFO`和`SCHED_RR`调度策略有何具体差异? 3. Windows下如何通过编程动态调整线程优先级? 4. 在多核CPU中,如何避免线程迁移导致的缓存抖动问题?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值