• c和cpp实现CPU核上绑定固定线程


    参考链接:

    C\C++实现CPU核上绑定固定线程\c++线程绑定cpu

    Android 高通骁龙CPU线程与CPU核的绑定

    C++thread线程绑定核并起名字

    【linux C】绑定任务到特定CPU(CPU亲和性)

    相关概念及工具

    CPU亲和性

    CPU亲和性(affinity) 就是进程要在某个给定的CPU上尽量长时间的运行而不被迁移到其他处理器的倾向性。
    linux内核进程调度器天生具有软CPU亲和性(affinity)的特性,着意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。
    Linux内核还包含一些机制,它让开发人员可以编程实现硬CPU亲和性(affinity)。着意味着应用程序可以显示的指定进程在那个(或那些)处理器上运行

    查看cpu有几个核

    命令查看

    $ cat /proc/cpuinfo |grep processor | wc -l
    # 或
    $ nproc
    
    # 使用cat /proc/cpuinfo可以更详细的查看哪些可以用,哪些不可以用
    # 如可以先禁用一个cpu1
    $ echo 0 > /sys/devices/system/cpu/cpu1/online
    $ cat /proc/cpuinfo
    # 再解除禁用,再对比cat /proc/cpuinfo的输出
    $ echo 1 > /sys/devices/system/cpu/cpu1/online
    $ cat /proc/cpuinfo
    # 输出已省略,可自行实验。可以看到,禁用的时候,是没有processor1的
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    疑问:cat /proc/cpuinfo |grep processor | wc -l 和 nproc 查看的核数不一样 :
    用cat /proc/cpuinfo |grep processor | wc -l 查看的是8个核
    用nproc 产看的是6个核

    代码查看

    #include 
    int sysconf(_SC_NPROCESSORS_CONF);/* 返回系统可以使用的核数,但是其值会包括系统中禁用的核的数目,因此该值并不代表当前系统中可用的核数 */
    int sysconf(_SC_NPROCESSORS_ONLN);/* 返回值真正的代表了系统当前可用的核数 */
    
    /* 以下两个函数与上述类似 */
    #include 
    int get_nprocs_conf (void);/* 可用核数 */
    int get_nprocs (void);/* 真正的反映了当前可用核数,比如你禁用的就不显示 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    代码讲解

    cpu集(cpu_set_t)

    cpu_set_t的使用

    cpu_set_t用来描述CPU的集合,被sched_setaffinity等类似的函数使用。

    非动态分配cpu_set_t

    常用接口声明(在glibc中用本质为宏定义):

           void CPU_ZERO(cpu_set_t *set);
           void CPU_SET(int cpu, cpu_set_t *set);
           void CPU_CLR(int cpu, cpu_set_t *set);
           int  CPU_ISSET(int cpu, cpu_set_t *set);
           int  CPU_COUNT(cpu_set_t *set);
           void CPU_AND(cpu_set_t *destset,
                        cpu_set_t *srcset1, cpu_set_t *srcset2);
           void CPU_OR(cpu_set_t *destset,
                        cpu_set_t *srcset1, cpu_set_t *srcset2);
           void CPU_XOR(cpu_set_t *destset,
                        cpu_set_t *srcset1, cpu_set_t *srcset2);
           int  CPU_EQUAL(cpu_set_t *set1, cpu_set_t *set2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    用法举例:

    cpu_set_t set1, set2;
    CPU_ZERO(&set1); //清空集合,即set1里不包含任何CPU,本质为所有bit清零
    CPU_ZERO(&set2); //清空集合,即set2里不包含任何CPU,本质为所有bit清零
    CPU_SET(0, &set1); //将cpu0添加到集合set1中,本质为对应bit置1
    CPU_SET(1, &set2); //将cpu1添加到集合set2中,本质为对应bit置1
    CPU_CLR(0, &set1); //将cpu0从集合set1中移除,本质为对应bit清零
    int ret = CPU_ISSET(1, &set2); //判断cpu1是否在集合set2中,在返回非零,不在返回0
    int cnt = CPU_COUNT(&set2); //返回集合set2中的CPU的个数
    cpu_set_t result;
    CPU_AND(&result, &set1, &set2); //set1和set2的所有bit按位与,结果存入result
    CPU_OR(); //按位或
    CPU_XOR(); //按位异或
    ret = CPU_EQUAL(&set1, &set2); //集合set1和集合set2相等的话,ret为非零,不相等,ret为0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    动态分配cpu_set_t

    刚开始接触cpu_set_t时,对_S系列接口有疑问,不明白它存在的意义,明明自己malloc一个cpu_set_t就可以,然后使用各种非_S对其操作,为什么非要有_S系列接口呢?具体原因见
    glibc源码粗讲解:绑定任务到特定CPU

    绑定任务到指定CPU

    CPU亲和性只是一种倾向性,当绑定的CPU不存在或者存在但是被禁用了,任务会在其他的CPU上执行。

    设置任务亲和性的接口有:

    • sched_setaffinity:修改指定pid_t的任务的亲和性
    • pthread_setaffinity_np:gnu接口,修改指定pthrad_t的任务的亲和性。
    • pthread_attr_setaffinity_np:gnu接口。创建线程前,通过线程属性结构体控制新线程的亲和性。
      注:np的意思为不可移植,即在非gnu的系统上,应该是没有这个接口的。

    pthread_setaffinity_np

    设置线程亲和性,将线程绑定到指定CPU核
    int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,const cpu_set_t *cpuset);
    thread:线程id
    cpusetsize:集合内存大小

    pthread_getaffinity_np

    获取指定线程的CPU集合
    int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset);
    thread:线程id
    cpusetsize:集合内存大小
    cpuset:CPU核的集合

    #define _GNU_SOURCE
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    void *myfun(void *arg)
    {
        cpu_set_t mask;
        cpu_set_t get;
        char buf[256];
        int i;
        int j;
        int num = sysconf(_SC_NPROCESSORS_CONF); // 在运行时获取配置信息,获取CPU核个数
        CPU_ZERO(&mask);    // 初始化,设为空
        printf("system has %d processor(s)\n", num);
        for(i = 0; i < num; i++){
            CPU_SET(i, &mask);  // 将某个cpu加入cpu集中
        }
        // 将线程绑定到cpu集中的各个CPU核,这样做不太好,会导致线程在各CPU核中切换,导致性能下降
        // 本次只为测试
        if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {
            fprintf(stderr, "set thread affinity failed\n");
        }
        CPU_ZERO(&get);
        if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) {
            fprintf(stderr, "get thread affinity failed\n");
        }
        for (i = 0; i < num; i++) {
            if (CPU_ISSET(i, &get)) {
                printf("thread %ld is running in processor %d\n", pthread_self(), i);
            }
            j = 0;
            while (j++ < 100000000){
                memset(buf, 0, sizeof(buf));
            }
        }
        pthread_exit(NULL);
    }
    int main(){
        int num = sysconf(_SC_NPROCESSORS_CONF);
        printf("CPU核数:%d \n", num);
        pthread_t tid;    //创建一个线程ID
        if (pthread_create(&tid, NULL, myfun, NULL) != 0) {   //myfun是线程的入口函数
            fprintf(stderr, "thread create failed\n");
            return -1;
        }
        pthread_join(tid, NULL); //回收线程资源
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    编译:
    g++ -o cpu CPU_Bind.cpp -lpthread
    运行:
    ./cpu

    结果:

    CPU核数:5
    system has 5 processor(s)
    thread 140197435995904 is running in processor 0
    thread 140197435995904 is running in processor 1
    thread 140197435995904 is running in processor 2
    thread 140197435995904 is running in processor 3
    thread 140197435995904 is running in processor 4

    sched_setaffinity

    如果考虑可移植性的话,推荐使用sched_setaffinity()函数将任务绑定到特定CPU执行。
    将当前的pid绑定到4,5,6,7核上(大核核超大核)

    #define _GNU_SOURCE
    #include 
    #include 
    #include 
    #include 
    
    int main(){
        cpu_set_t cpumask;
        CPU_ZERO(&cpumask);
        CPU_SET(4, &cpumask);
        CPU_SET(5, &cpumask);
        CPU_SET(6, &cpumask);
        CPU_SET(7, &cpumask);
        printf("pid = %d\n",getpid());
        sched_setaffinity(getpid(), sizeof(cpumask), &cpumask);
        while(1)
        {
          sleep(1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    编译:
    gcc -o bind_cpu cpu.c -pthread
    运行:
    ./bind_cpu
    新开一个终端,运行ps -eo pid,args,psr | grep bind_cpu可以查看bind_cpu跑在哪个核上。

  • 相关阅读:
    Kubernetes - Kubernetes详解;安装部署(一)
    基于HTML+CSS制作一个简单的家乡网页制作作业,广州介绍旅游网页设计代码 学生个人html静态网页制作成品代码
    云安全—kubelet攻击面
    1300*C. Coin Rows(枚举&模拟)
    【网站项目】“最多跑一次”小程序
    -级数求和-
    css自定义属性
    人脸关键点COFW-68使用指南
    清华训练营悟道篇之操作系统的调用接口
    院内导航系统哪家口碑好?医疗院内导航地图公司排名
  • 原文地址:https://blog.csdn.net/weixin_42581177/article/details/127553824