• linux C++实现线程绑定CPU


    前言

    嵌入式里面我们会使用到多核的cpu,随着产品芯片性能提升,我们也会有很多功能,以及很多进程产生运行,这个时候我们在任务调度调优的时候,把一些进程绑定到固定cpu运行,下面就来分享一下cpu绑定运行的过程:
    首先运行的环境需要多核,大家可以按照下面命令进行查询对应设备的cpu数量
    使用cat /proc/cpuinfo查看cpu信息,如下两个信息:
    processor,指明第几个cpu处理器
    cpu cores,指明每个处理器的核心数
    请添加图片描述

    基本概念

    cpu亲和性(affinity)

    CPU的亲和性, 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,也称为CPU关联性;再简单的点的描述就将指定的进程或线程绑定到相应的cpu上;在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了,当绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,性能有一定的提高。
    软亲和性(affinity)
    就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。
    硬亲和性(affinity)
    简单来说就是利用linux内核提供给用户的API,强行将进程或者线程绑定到某一个指定的cpu核运行。

    代码绑定实现

    上面我们使用cat /proc/cpuinfo命令查询了自己设备的CPU,以我为例,我的电脑从0~7一共有8核。
    下面是代码的demo:

    #include 
    #include 
    #include 
    
    void thread_func1()
    {
            cpu_set_t mask;
            CPU_ZERO(&mask);
            CPU_SET(1, &mask); //指定该线程使用的CPU
            if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {
                    perror("pthread_setaffinity_np");
            }
            int count = 0;
            while(1)
            {
                    count ++;
                    sleep(1);
                    printf("fun 1 cnt :%d \n",count);
                    for(int i = 0; i < 8; i++) {
                            if (CPU_ISSET(i, &mask))  //查看cpu i 是否在get 集合当中
                            {
                                    printf("1 this process %d of running processor: %d\n", getpid(), i);
                            }
                    }
            }
    }
    
    void thread_func2()
    {
            int count = 0;
            cpu_set_t mask;
            CPU_ZERO(&mask);
            CPU_SET(5, &mask);
            if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {
                    perror("pthread_setaffinity_np");
            }
            while(1)
            {
                    usleep(1000*1000);
                    count ++;
                    printf("fun 2 cnt :%d \n",count);
                    for(int i = 0; i < 8; i++) {
                     if (CPU_ISSET(i, &mask))  //查看cpu i 是否在get 集合当中
                    {
                           printf("2 this process %d of running processor: %d\n", getpid(), i);
    
                        }
                    }
    
            }
    
    }
    
    int main(int argc, char *argv[])
    {
          int cpus = 0;
    
            cpus = sysconf(_SC_NPROCESSORS_CONF);
    
            printf("cpus: %d\n", cpus); //查看cpu的个数;
    
    
        cpu_set_t mask;
        CPU_ZERO(&mask);
        CPU_SET(7, &mask);
        if (sched_setaffinity(0, sizeof(mask), &mask) < 0) {
            perror("sched_setaffinity");
        }
    
        std::thread t1(thread_func1);
        std::thread t2(thread_func2);
            usleep(1000); /* 让当前的设置有足够时间生效*/
           while(1)
            {
                    /*查看运行在当前进程的cpu*/
                    sleep(1); /* 让当前的设置有足够时间生效*/
                    printf("fun main \n");
                    for(int i = 0; i < cpus; i++) {
    
                            if (CPU_ISSET(i, &mask))  //查看cpu i 是否在get 集合当中
                            {
                                    printf("3 this process %d of running processor: %d\n", getpid(), i);
                            }
                    }
            }
        t1.join();
        t2.join();
    }
    
    • 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
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    上面一共运行了三个线程,一个是main 主线程,还有两个是自己定义的thread。
    最重要的设置代码就是下面所示:设置cpu 亲和

    cpu_set_t mask;
    CPU_ZERO(&mask);/* 初始化set集,将set置为空*/
    CPU_SET(5, &mask);/* 将对应的cpu序号加入到集合*/
    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) /*设置cpu 亲和性(affinity)*/
    { 
        perror("pthread_setaffinity_np");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    执行代码
    请添加图片描述

    代码绑定查看

    使用ps -ef | grep a.out 命令查看对应的PID
    请添加图片描述使用 top命令查看对应pid的线程详细信息 top -p 14617请添加图片描述在进入top命令后,继续输入 f
    请添加图片描述使用上下 移动高亮到p
    请添加图片描述空格 选中
    再按q 退出显示
    输入大写H
    就可以看到对应线程数据了请添加图片描述

    注意事项

    虽然绑核技术可以提高程序性能,但也需要注意以下几点:

    1. 不要过度绑定:过度绑定可能会出现线程之间的竞争和CPU利用率低下的情况。

    2. 绑定前需要评估:在进行核心绑定之前,需要对程序进行评估,以确定性能瓶颈位置和绑定的核心数。

    3. 不要跨核心访问内存:如果一个进程已经绑定到一个核心上,那么该进程所使用的内存也只应该在该核心专用的内存上进行操作。如果在不同核心之间频繁地进行内存操作,则会影响程序的性能。

    引用https://blog.csdn.net/lyn631579741/article/details/123337907

  • 相关阅读:
    深度学习的模型压缩与加速(万字长文带你入门)
    Spring的创建和使用
    Java 相关高频面试解析
    ROS1云课→27机器人SLAM小结
    数学建模整理-层次分析法
    python KNN分类算法实战(使用鸢尾花数据集)
    经典二分法查找的进阶题目——LeetCode33 搜索旋转排序数组
    2022李宏毅作业hw1—新冠阳性人员数量预测。
    Parsing error: The keyword ‘interface‘ is reserved配置优化
    Kafka安装启动(含安装包)
  • 原文地址:https://blog.csdn.net/lzy6041/article/details/134262271