將進程/線程與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