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的

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

代码查看

#include <unistd.h>
int sysconf(_SC_NPROCESSORS_CONF);/* 返回系统可以使用的核数,但是其值会包括系统中禁用的核的数目,因此该值并不代表当前系统中可用的核数 */
int sysconf(_SC_NPROCESSORS_ONLN);/* 返回值真正的代表了系统当前可用的核数 */

/* 以下两个函数与上述类似 */
#include <sys/sysinfo.h>
int get_nprocs_conf (void);/* 可用核数 */
int get_nprocs (void);/* 真正的反映了当前可用核数,比如你禁用的就不显示 */

代码讲解

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);

用法举例:

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
动态分配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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>

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;
}

编译:
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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>

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;
}

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

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Windows下,可以使用`SetThreadAffinityMask`函数将线程绑定到指定的CPU核心上。以下是一个示例代码,演示了如何使用C++11标准将线程绑定到指定CPU核心上: ```cpp #include <iostream> #include <thread> #include <Windows.h> void bindThreadToCore(std::thread& thread, int core) { // 获取线程的原生句柄 HANDLE nativeThread = thread.native_handle(); // 将线程绑定到指定的CPU核心 DWORD_PTR affinityMask = (DWORD_PTR)1 << core; DWORD_PTR previousMask = SetThreadAffinityMask(nativeThread, affinityMask); // 检查绑定是否成功 if (previousMask == 0) { std::cerr << "Failed to bind thread to CPU core " << core << std::endl; } else { std::cout << "Thread bound to CPU core " << core << std::endl; } } void myFunction() { // 在这里执行需要绑定在特定CPU核心上的操作 } int main() { int core = 0; // 要绑定CPU核心号 std::thread threadObj(myFunction); bindThreadToCore(threadObj, core); // 等待线程完成 threadObj.join(); return 0; } ``` 在这个示例代码中,我们首先定义了一个名为`bindThreadToCore`的函数,它接受一个`std::thread`对象和一个CPU核心号作为参数。在`bindThreadToCore`函数中,我们首先使用`std::thread::native_handle()`函数获取线程的原生句柄。然后,我们使用`SetThreadAffinityMask`函数将线程绑定到指定的CPU核心上。 在`main`函数中,我们创建了一个名为`threadObj`的`std::thread`对象,并调用`bindThreadToCore`函数将该线程对象绑定到指定的CPU核心上。然后,我们等待线程完成,最后输出绑定CPU核心号。 需要注意的是,该方法是Windows特定的,并且依赖于Windows API函数`SetThreadAffinityMask`。请确保在Windows环境下编译和运行该代码。 希望这可以帮助到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值