linux下浅谈线程绑定cpu
2014-10-16 19:04
381 查看
1.线程绑定的关键API:
关于linux下线程绑定的api在网上资料很多,关键是用到两个系统API:int pthread_setaffinity_np(pthread_tthread,
size_t cpusetsize,const
cpu_set_t *cpuset);
int pthread_getaffinity_np(pthread_tthread,
size_t cpusetsize, cpu_set_t
*cpuset);
从函数名以及参数名都很明了,唯一需要点解释下的可能就是cpu_set_t这个结构体。这个结构体的理解类似于select中的fd_set,可以理解为cpu集,也是通过约定好的宏来进行清除、设置以及判断:
//初始化,设为空
void CPU_ZERO (cpu_set_t *set);
//将某个cpu加入cpu集中
void CPU_SET (int cpu, cpu_set_t *set);
//将某个cpu从cpu集中移出
void CPU_CLR (int cpu, cpu_set_t *set);
//判断某个cpu是否已在cpu集中设置了
int CPU_ISSET (int cpu, const cpu_set_t *set);
cpu集可以认为是一个掩码,每个设置的位都对应一个可以合法调度的 cpu,而未设置的位则对应一个不可调度的
CPU。
注意这个cpu集是一个集合,最终系统决定选定线程在哪个cpu上执行,选定标准我暂时没了解。最终选定一个cpu上执行
而不是在多个cpu上执行。
2.线程绑定的目的:
线程绑定的主要目的是提高线程访问cpu的cache(缓存)命中率,从而提高程序的并行性能。线程绑定的并行优化程度和服务器架构有密切关系。传统的多核运算是使用SMP(Symmetric
Multi-Processor )模式:将多个处理器与一个集中的存储器和I/O总线相连。所有处理器只能访问同一个物理存储器,因此SMP系统有时也被称为一致存储器访问(UMA)结构体系,一致性意指无论在什么时候,处理器只能为内存的每个数据保持或共享唯一一个数值。很显然,SMP的缺点是可伸缩性有限,因为在存储器和I/O接口达到饱和的时候,增加处理器并不能获得更高的性能。
NUMA模式是一种分布式存储器访问方式,处理器可以同时访问不同的存储器地址,大幅度提高并行性。 NUMA模式下,处理器被划分成多个"节点"(node), 每个节点被分配有的本地存储器空间。 所有节点中的处理器都可以访问全部的系统物理存储器,但是访问本节点内的存储器所需要的时间,比访问某些远程节点内的存储器所花的时间要少得多。因此NUMA架构相比SMP架构上使用线程绑定的方式更能提高并行效率。
3.linux下线程绑定小例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sched.h> #include <sched.h> #define MAX_PROCESSNUM 256 pthread_mutex_t mutex; int nProcessNum = 0; int GetProcessNum() { return sysconf(_SC_NPROCESSORS_CONF); } void *myfunc(void *arg) { int bindcpunum = *(int *)arg; cpu_set_t mask; cpu_set_t get; int j = 0; char buf[256]; CPU_ZERO(&mask); CPU_SET(bindcpunum,&mask); 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 (j = 0;j < nProcessNum;j++) { if (CPU_ISSET(j,&get)) printf("thread %d is running in processor%d\n",pthread_self(),j); } pthread_mutex_lock(&mutex); printf("thread %d is working ...\n",bindcpunum); pthread_mutex_unlock(&mutex); for (j = 0; j < 100000;j++) memset(buf,0,sizeof(buf)); pthread_mutex_lock(&mutex); printf("thread %d is done\n",bindcpunum); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } int main(int argc,char *argv[]) { int cpubindset[MAX_PROCESSNUM] = {0}; pthread_mutex_init(&mutex,NULL); pthread_t tid[MAX_PROCESSNUM]; nProcessNum = GetProcessNum(); for (int i = 0; i < nProcessNum; i++) { cpubindset[i] = i; if (pthread_create(&tid[i],NULL,&myfunc,(void *)&cpubindset[i]) != 0) { fprintf(stderr,"thread create failed\n"); return -1; } } for (int i = 0 ;i < nProcessNum; i++) pthread_join(tid[i],NULL); return 0; }
实例分析:
例子用系统API获取当前系统的处理器核数,根据这个核数创建对应的n个线程,并把这n个线程绑定到对应的cpu上,
并循环100000次执行一个费时操作memset(buf,0,sizeof(buf));
例子中使用互斥量保证线程同步,我测试时把费时操作外面又加了一层循环,然后执行测试程序,用top发现程序cpu
所占比例为1600%(我的机器有16个cpu核),证明每个cpu都在跑那个费时操作。
4.疑问:
1.使用线程绑定机制和不使用线程绑定到底区别在哪?我个人觉得在使用线程绑定机制时,当多个线程都需要访问相同的数据,可以把这些线程都绑定到一个cpu上,提高cache
的命中率。
不使用线程绑定时线程分配到哪个cpu是由操作系统负责的,操作系统可能根据当前的cpu状态来进行调度,把空闲的cpu分给新创建的
线程
相关文章推荐
- linux下浅谈线程绑定cpu
- linux 线程与CPU绑定
- Linux中线程与CPU核的绑定
- Linux中线程与CPU核的绑定
- Linux_多核系统下绑定进程或线程到指定CPU_核执行和调优
- linux下将不同线程绑定到不同core和cpu上 pthread_setaffinity_np——005最全面
- linux下将不同线程绑定到不同core和cpu上 pthread_setaffinity_np
- Linux中线程与CPU核的绑定
- Linux中线程与CPU核的绑定
- Linux中线程与CPU核的绑定
- Linux中线程与CPU核的绑定
- Linux中线程与CPU核的绑定
- Linux进程或线程绑定到CPU
- linux下将不同线程绑定到不同core和cpu上 pthread_setaffinity_np
- Linux中线程与CPU核的绑定
- Linux中线程与CPU核的绑定
- Linux中线程与CPU核的绑定
- Linux_多核系统下绑定进程或线程到指定CPU_核执行
- linux线程绑定到指定cpu运行
- LINUX将制定的线程号、进程号绑定到指定CPU核上