cpu绑定和cpu亲和性

将进程/线程与cpu绑定,最直观的好处就是提高了cpu cache的命中率,从而减少内存访问损耗,提高程序的速度。我觉得在NUMA架构下,这个操作对系统运行速度的提升有较大的意义,而在SMP架构下,这个提升可能就比较小。这主要是因为两者对于cache、总线这些资源的分配使用方式不同造成的,NUMA每个cpu有自己的一套资源体系, SMP中每个核心还是需要共享这些资源的,从这个角度来看,NUMA使用cpu绑定时,每个核心可以更专注地处理一件事情,资源体系被充分使用,减少了同步的损耗。SMP由于一部分资源的共享,在进行了绑定操作后,受到的影响还是很大的。

通过linux提供的几个api, 可以轻松地完成这个优化:

 

[cpp] view plaincopy
 
  1. #define _GNU_SOURCE               
  2. #include <sched.h>  
  3.   
  4. int sched_setaffinity(pid_t pid, size_t cpusetsize,cpu_set_t *mask);    //设定pid 绑定的cpu,   
  5. int sched_getaffinity(pid_t pid, size_t cpusetsize,cpu_set_t *mask);    //查看pid 绑定的cpu。  

[cpp] view plaincopy
 
  1. cpu_set_t  //是一个掩码数组,一共有1024位,每一位都可以对应一个cpu核心  
  2. //以下宏,都是对这个掩码进行操作的。如果需要,一个进程是可以绑定多个cpu的。  
  3. void CPU_ZERO(cpu_set_t *set);  
  4. void CPU_SET(int cpu, cpu_set_t *set);  
  5. void CPU_CLR(int cpu, cpu_set_t *set);  
  6. int CPU_ISSET(int cpu, cpu_set_t *set);  
  7.    

 

下面是一个实例。

 

[cpp] view plaincopy
 
  1. /* 
  2.  * @FileName: simple_affinity.c 
  3.  * @Author: wzj 
  4.  * @Brief:  
  5.  * 1. cpu affinity.  case 
  6.  * 2.在子线程中,会继承绑定的cpu..., 不过在子线程中,可以重新分配。 
  7.  *   
  8.  * @History:  
  9.  *  
  10.  *  
  11.  *  
  12.  * @Date: 2012年04月21日星期六12:56:14 
  13.  *  
  14.  */   
  15.   
  16. #include <stdlib.h>  
  17. #include <stdio.h>  
  18. #include <unistd.h>  
  19.   
  20. #define __USE_GNU       //启用CPU_ZERO等相关的宏  
  21. //#define _GNU_SOURCE  
  22. #include <sched.h>  
  23. #include <pthread.h>            //这个东西原来放在__USE_GNU宏之前,结果被编译器报错说CPU_ZERO未定义  
  24.   
  25. void* new_test_thread(void* arg)  
  26. {  
  27.     cpu_set_t mask;  
  28.     int i = 0;  
  29.     int num = sysconf(_SC_NPROCESSORS_CONF);    //获取当前的cpu总数  
  30.     pthread_detach(pthread_self());  
  31.       
  32.     CPU_ZERO(&mask);      
  33.     CPU_SET(1, &mask);      //绑定cpu 1  
  34.     if(sched_setaffinity(0, sizeof(mask), &mask) == -1)      //0 代表对当前线程/进程进行设置。  
  35.     {  
  36.         printf("set affinity failed..");  
  37.     }  
  38.     while(1)  
  39.     {  
  40.         CPU_ZERO(&mask);  
  41.         if(sched_getaffinity(0, sizeof(mask), &mask) == -1)   
  42.         {  
  43.             printf("get failed..\n");  
  44.         }  
  45.   
  46.         for(i = 0; i < num; i++)  
  47.         {  
  48.             if(CPU_ISSET(i, &mask))  
  49.             printf("new thread %d run on processor %d\n", getpid(), i);  
  50.         }  
  51.         while(1);  
  52.         sleep (1);  
  53.     }  
  54. }      //while(1);      //如果觉得不明显,改成这个,  

 

[cpp] view plaincopy
 
  1. void* child_test_thread(void* arg)  
  2. {  
  3.     cpu_set_t mask;  
  4.     int i = 0;  
  5.     int num = sysconf(_SC_NPROCESSORS_CONF);  
  6.     pthread_detach(pthread_self());  
  7.       
  8.     while(1)  
  9.     {  
  10.         CPU_ZERO(&mask);  
  11.         if(sched_getaffinity(0, sizeof(mask), &mask) == -1)   
  12.         {  
  13.             printf("get failed..\n");  
  14.         }  
  15.   
  16.         for(i = 0; i < num; i++)  
  17.         {  
  18.             if(CPU_ISSET(i, &mask))  
  19.             printf("child thread %d run on processor %d\n", getpid(), i);  
  20.         }  
  21.         sleep (1);  
  22.     }  
  23.   
  24. }  
  25.   
  26. int  
  27. main(int argc, char* argv[])  
  28. {  
  29.     int num = sysconf(_SC_NPROCESSORS_CONF);  
  30.     int created_thread = 0;  
  31.     int myid;  
  32.     int i;  
  33.     int j = 0;  
  34.     pthread_t ptid = 0;  
  35.   
  36.     cpu_set_t mask;  
  37.     cpu_set_t get;  
  38.   
  39.     if(argc != 2)  
  40.     {  
  41.         printf("usage: ./cpu num\n");  
  42.         return -1;  
  43.     }  
  44.     myid = atoi(argv[1]);  
  45.     printf("system has %i processor(s).\n", num);  
  46.   
  47.     CPU_ZERO(&mask);  
  48.     CPU_SET(myid, &mask);  
  49.     if(sched_setaffinity(0, sizeof(mask), &mask) == -1)  
  50.     {  
  51.         printf("warning: set CPU affinity failed...");  
  52.     }  
  53.   
  54.     int ret = pthread_create(&ptid, NULL, new_test_thread, NULL);  
  55.     if(ret)  
  56.     {  
  57.         return -1;  
  58.     }  
  59.     ret = pthread_create(&ptid, NULL, child_test_thread, NULL);  
  60.     if(ret)  
  61.     {  
  62.         return -1;  
  63.     }  
  64.   
  65.   
  66.     while(1)  
  67.     {  
  68.         CPU_ZERO(&get);  
  69.         if(sched_getaffinity(0, sizeof(get), &get) == -1)  
  70.         {  
  71.             printf("can't get cpu affinity...");  
  72.         }  
  73.   
  74.         for(i = 0; i < num; i++)  
  75.         {  
  76.             if(CPU_ISSET(i, &get))  
  77.             {  
  78.                 printf("this process %d is runing on procesor:%d\n", getpid(), i);  
  79.             }  
  80.         }  
  81.           
  82.         sleep(1);  
  83.     }  
  84.     //while(1); //使用这个更明显  
  85.     return 0;  
  86. }  

 

 

编译:

 

[cpp] view plaincopy
 
  1. gcc -o cpu simple_affinity.c -lpthread  


执行./cpu [cpu num / masks] ,使用top观察cpu使用状况。 使用./cpu 0 时,可以发现,两颗核心使用率都比较高, 使用./cpu 1时,可以发现,1核的压力比较重。

 

 

当然还可以对线程进行cpu绑定。

 

[cpp] view plaincopy
 
  1. #define _GNU_SOURCE  
  2. #include <pthread.h>  
  3.    
  4. int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,  
  5.                           const cpu_set_t *cpuset);  
  6. int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize,  
  7.                           cpu_set_t *cpuset);  



 

这个介绍了使用的时机,比较经典:http://www.ibm.com/developerworks/cn/linux/l-affinity.html

posted on 2015-11-19 13:23  DayAfterDay  阅读(2012)  评论(0编辑  收藏  举报

导航