linux下浅谈线程绑定cpu

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分给新创建的

线程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值