将进程/线程与cpu绑定,最直观的好处就是提高了cpu cache的命中率,从而减少内存访问损耗,提高程序的速度。我觉得在NUMA架构下,这个操作对系统运行速度的提升有较大的意义,而在SMP架构下,这个提升可能就比较小。这主要是因为两者对于cache、总线这些资源的分配使用方式不同造成的,NUMA每个cpu有自己的一套资源体系, SMP中每个核心还是需要共享这些资源的,从这个角度来看,NUMA使用cpu绑定时,每个核心可以更专注地处理一件事情,资源体系被充分使用,减少了同步的损耗。SMP由于一部分资源的共享,在进行了绑定操作后,受到的影响还是很大的。
通过linux提供的几个api, 可以轻松地完成这个优化:
#include <sched.h>
int sched_setaffinity(pid_t pid, size_t cpusetsize,cpu_set_t *mask); //设定pid 绑定的cpu,
int sched_getaffinity(pid_t pid, size_t cpusetsize,cpu_set_t *mask); //查看pid 绑定的cpu。
cpu_set_t //是一个掩码数组,一共有1024位,每一位都可以对应一个cpu核心
//以下宏,都是对这个掩码进行操作的。如果需要,一个进程是可以绑定多个cpu的。
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);
下面是一个实例。
- /*
- * @FileName: simple_affinity.c
- * @Author: wzj
- * @Brief:
- * 1. cpu affinity. case
- * 2.在子线程中,会继承绑定的cpu..., 不过在子线程中,可以重新分配。
- *
- * @History:
- *
- *
- *
- * @Date: 2012年04月21日星期六12:56:14
- *
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <unistd.h>
- #define __USE_GNU //启用CPU_ZERO等相关的宏
- //#define _GNU_SOURCE
- #include <sched.h>
- #include <pthread.h> //这个东西原来放在__USE_GNU宏之前,结果被编译器报错说CPU_ZERO未定义
- void* new_test_thread(void* arg)
- {
- cpu_set_t mask;
- int i = 0;
- int num = sysconf(_SC_NPROCESSORS_CONF); //获取当前的cpu总数
- pthread_detach(pthread_self());
- CPU_ZERO(&mask);
- CPU_SET(1, &mask); //绑定cpu 1
- if(sched_setaffinity(0, sizeof(mask), &mask) == -1) //0 代表对当前线程/进程进行设置。
- {
- printf("set affinity failed..");
- }
- while(1)
- {
- CPU_ZERO(&mask);
- if(sched_getaffinity(0, sizeof(mask), &mask) == -1)
- {
- printf("get failed..\n");
- }
- for(i = 0; i < num; i++)
- {
- if(CPU_ISSET(i, &mask))
- printf("new thread %d run on processor %d\n", getpid(), i);
- }
- while(1);
- sleep (1);
- }
- } //while(1); //如果觉得不明显,改成这个,
- void* child_test_thread(void* arg)
- {
- cpu_set_t mask;
- int i = 0;
- int num = sysconf(_SC_NPROCESSORS_CONF);
- pthread_detach(pthread_self());
- while(1)
- {
- CPU_ZERO(&mask);
- if(sched_getaffinity(0, sizeof(mask), &mask) == -1)
- {
- printf("get failed..\n");
- }
- for(i = 0; i < num; i++)
- {
- if(CPU_ISSET(i, &mask))
- printf("child thread %d run on processor %d\n", getpid(), i);
- }
- sleep (1);
- }
- }
- int
- main(int argc, char* argv[])
- {
- int num = sysconf(_SC_NPROCESSORS_CONF);
- int created_thread = 0;
- int myid;
- int i;
- int j = 0;
- pthread_t ptid = 0;
- cpu_set_t mask;
- cpu_set_t get;
- if(argc != 2)
- {
- printf("usage: ./cpu num\n");
- return -1;
- }
- myid = atoi(argv[1]);
- printf("system has %i processor(s).\n", num);
- CPU_ZERO(&mask);
- CPU_SET(myid, &mask);
- if(sched_setaffinity(0, sizeof(mask), &mask) == -1)
- {
- printf("warning: set CPU affinity failed...");
- }
- int ret = pthread_create(&ptid, NULL, new_test_thread, NULL);
- if(ret)
- {
- return -1;
- }
- ret = pthread_create(&ptid, NULL, child_test_thread, NULL);
- if(ret)
- {
- return -1;
- }
- while(1)
- {
- CPU_ZERO(&get);
- if(sched_getaffinity(0, sizeof(get), &get) == -1)
- {
- printf("can't get cpu affinity...");
- }
- for(i = 0; i < num; i++)
- {
- if(CPU_ISSET(i, &get))
- {
- printf("this process %d is runing on procesor:%d\n", getpid(), i);
- }
- }
- sleep(1);
- }
- //while(1); //使用这个更明显
- return 0;
- }
编译:
- gcc -o cpu simple_affinity.c -lpthread
执行./cpu [cpu num / masks] ,使用top观察cpu使用状况。 使用./cpu 0 时,可以发现,两颗核心使用率都比较高, 使用./cpu 1时,可以发现,1核的压力比较重。
当然还可以对线程进行cpu绑定。
- #define _GNU_SOURCE
- #include <pthread.h>
- int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
- const cpu_set_t *cpuset);
- int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize,
- cpu_set_t *cpuset);
这个介绍了使用的时机,比较经典:http://www.ibm.com/developerworks/cn/linux/l-affinity.html