zoukankan      html  css  js  c++  java
  • posix多线程有感线程高级编程(均衡负载CPU绑定)

            多线程、多进程的情况下。可以同过指定CPU进行负载均衡,而不是让操作系统自动进行负载均衡。因为你比操作系统更了解自己的程序,为了避免调度器愚蠢的调度你的程序,或是为了在多线程程序中避免缓存失效造成的开销。

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

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

    #define _GNU_SOURCE             
    #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);

          函数中pid表示需要设置或获取绑定信息的线程id(或进程id),如果为0,表示对当前调用的线程进行设置;第2个参数cpusetsize一般设置为sizeof(cpu_set_t),用以表示第3个参数指向的内存结构对象的大小;第3个参数mask指向类型为cpu_set_t对象的指针,用以设置或获取指定线程可以使用的CPU核列表。Linux提供函数CPU_ZERO、CPU_SET和CPU_ISSET对cpu_set_t类型的对象进行操作,其中CPU_ZERO用于清空cpu_set_t类型对象的内容,CPU_SET用于设置cpu_set_t类型对象,CPU_ISSET用于判断cpu_set_t类型对象与核对应的位是否被设置。

    下面是一个实例。

    #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;
    }

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

    特别注意:

    #define __USE_GNU不要写成#define _USE_GNU

    #include<pthread.h>必须写在#define __USE_GNU之后,否则编译会报错

    查看你的线程情况可以在执行时在另一个窗口使用top -H来查看线程的情况,查看各个核上的情况请使用top命令然后按数字“1”来查看。

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

    1. #define _GNU_SOURCE   
    2. #include <pthread.h>   
    3.    
    4. int pthread_setaffinity_np(pthread_t threadsize_t cpusetsize,  
    5.                           const cpu_set_t *cpuset);  
    6. int pthread_getaffinity_np(pthread_t threadsize_t cpusetsize,  
    7.                           cpu_set_t *cpuset);  
    int bind2cpu(int cpu_index)
    {
    	cpu_set_t set;
    	cpu_set_t get;
    	int cpu_num = sysconf(_SC_NPROCESSORS_CONF);
    	if(cpu_index >= cpu_num)
    		return -1;
    
    	CPU_ZERO(&set);
    	CPU_SET(cpu_index, &set);
    	if (pthread_setaffinity_np(pthread_self(), sizeof(set), &set) < 0) {
    		perror( "set thread affinity failed");
    		return -2;
    	}
    #if 1
    	CPU_ZERO(&get);
    	if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) {
    		perror("get thread affinity failed");
    		return -3;
    	}
    	int j;
    	
    	for (j = 0; j < cpu_num; j++) {
    		if (CPU_ISSET(j, &get)) {
    			printf("the thread is running in processor %d\n", j);
    		}
    	}
    #endif
    	return 0;
    }

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

    进程/线程指定函数:sched_setaffinity(); sched_getaffinity();
    线程指定函数:pthread_setaffinity_np(); pthread_getaffinity_np();
     
    //#define __USE_GNU
    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    #include <sched.h>
    #include <unistd.h>
    #include <error.h>
    
    int main(int argc, char **argv)
    {
            int i = 0;
            int setid = 0;
            int prcs_num = 0;
            cpu_set_t mask;
    
            if (argc == 2)
                    setid = atoi(argv[1]);
    
            prcs_num = sysconf(_SC_NPROCESSORS_CONF);
            printf("System has %d processor(s).\n", prcs_num);
    
            CPU_ZERO(&mask);
            CPU_SET(setid, &mask);
            if (-1 == sched_setaffinity(0, sizeof(mask), &mask))
            {
                    perror("sched_setaffinity");
                    exit(-1);
            }
            CPU_ZERO(&mask);
            if (-1 == sched_getaffinity(0, sizeof(mask), &mask))
            {
                    perror("sched_getaffinity");
                    exit(-1);
            }
            for (i = 0; i < prcs_num; ++i)
                    if (CPU_ISSET(i, &mask))
                            printf("The process %d is running in processor %d\n", getpid(), i);
            exit(0);
    }

  • 相关阅读:
    二维码的生成细节和原理
    java写入文件的几种方法分享
    实例讲解虚拟机3种网络模式(桥接、nat、Host-only)
    ARM平台安装Docker的方法
    ARM 平台Docker运行RabbitMQ 以及迁移的简单办法
    Oracle12c(未更新任何补丁) 使用compression=all 参数导出之后导入失败
    CentOS7 通过移植二进制文件的方式安装redis、nginx以及dotnet core的简单办法
    Oracle 以及 达梦数据库简单查询所有表行数的存储过程
    Java内存模型(转载)
    深入探讨 Java 类加载器(转载)
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3072063.html
Copyright © 2011-2022 走看看