pthreads的性能差异

我正在编写性能敏感的代码。 我实现了一个简单的调度程序来分配工作负载,主线程负责调度程序。

cpu_set_t cpus; pthread_attr_t attr; pthread_attr_init(&attr); for(int i_group =0; i_group<n_groups; i_group++){ std::cout << i_t<< "\t"<<i_group << "th group of cpu" <<std::endl; for(int i =index ; i < index+group_size[i_group]; i++){ struct timeval start, end; double spent_time; gettimeofday(&start, NULL); arguments[i].i_t=i_t; arguments[i].F_x=F_xs[i_t]; arguments[i].F_y=F_ys[i_t]; arguments[i].F_z=F_zs[i_t]; CPU_ZERO(&cpus); CPU_SET(arguments[i].thread_id, &cpus); int err= pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpus); if(err!=0){ std::cout << err <<std::endl; exit(-1); } arguments[i].i_t=i_t; pthread_create( &threads[i], &attr, &cpu_work, &arguments[i]); gettimeofday(&end, NULL); spent_time = ((end.tv_sec - start.tv_sec) * 1000000u + end.tv_usec - start.tv_usec) / 1.e6; std::cout <<"create: " << spent_time << "s " << std::endl; } i_t++; cpu_count++; arr_finish[i_group]=false; } } 

像上面的主线程创build。 为简单的解释,我会假设i_group = 1。 孩子的线程划分和征服了一堆matrixmatrix乘法。 这里rank是指thread_id。

 int local_first = size[2]*( rank -1 )/n_compute_thread ; int local_end = size[2] * rank/n_compute_thread-1; //mkl_set_num_threads_local(10); gettimeofday(&start, NULL); for(int i_z=local_first; i_z<=local_end; i_z++ ){ cblas_dgemm( CblasColMajor, CblasNoTrans, CblasNoTrans, size[0], size[1], size[0], 1.0, F_x, size[0], rho[i_z], size[1], 0.0, T_gamma[i_z], size[1] ); } for(int i_z=local_first; i_z<=local_end; i_z++ ){ cblas_dgemm( CblasColMajor, CblasNoTrans, CblasNoTrans, size[0], size[1], size[1], 1.0, T_gamma[i_z], size[0], F_y, size[1], 0.0, T_gamma2[i_z], size[0] ); } gettimeofday(&end, NULL); std::cout <<i_t <<"\t"<< arg->thread_id <<"\t"<< sched_getcpu()<< "\t" << "compute: " <<spent_time << "s" <<std::endl; 

即使工作负载公平分配,每个线程的性能差别也很大。 看到下面的结果

5 65 4 4计算:0.270229s

5 64 1 1计算:0.284958s

5 65 2 2计算:0.741197s

5 65 3 3计算:0.76302s

第二列显示在特定线程中完成了多lessmatrix – matrix乘法。 最后一栏显示消耗的时间。 当我首先看到这个结果的时候,我认为它和线程的亲和性有关。 因此,我添加了几行来控制线程的绑定。 但是,并没有改变最后一栏的趋势。

我的电脑有20个物理核心和20个虚拟核心。 我只做了4个子线程来testing。 当然,它是在一台Linux机器上testing的。

为什么线程的性能变化如此之大? 和如何解决它?

首先,你是否真的创建了一个调度程序? 您的代码示例建议您使用Linux调度程序,并设置线程属性对象和线程相关性参数等。这种区别与选择如何解决问题相关。

无论如何,这个问题是很大的,还有几个问题/主题可以提出来帮助澄清条件,并且接近真正的答案。 开始,这里有一些事情要考虑:

1 – 基准测试的长度。 线程池内的线程性能的次级评估似乎是不够的。 延长评估时间以使调度程序的时间得以解决。 也许几分钟。
(有关现有基准测试实用程序中使用的典型持续时间的示例, 请阅读此部分

2 – 线程优先级。 你的线程不是唯一的。 内核调度程序是否有可能定期将基准线作为属于其他进程的线程(不是您创建的进程)具有更高的优先级? (因此正在取代你的,导致任务完成时间偏差)

3 – 任务的大小。 完成每个任务所需的操作数量是否足够小以适应调度程序分配的时间片? 这可能会导致线程对线程性能问题的感知,特别是如果每​​个任务之间的操作数量有任何差异。 ( 超过分配的CPU时间片的进程会自动下移到较低的“层”,而进行I / O请求或块的进程将被移动到更高的“层”。

4 – 平等的任务 – 你提到分而治之一堆矩阵矩阵乘法 。 但是这些矩阵的大小和内容是否相同? 即你确定每个任务的操作次数是否等于所有其他任务的操作次数? 由调度器分配给每个同等优先级的线程的时间片将确保随着时间的推移,具有大于该时间片的操作数的任务可以在单个时间片中完成,这将更容易延长完成时间(由于更高的优先级的其他操作系统进程)比那些没有足够的操作,以在一个时间片内适应舒适。

5 – 其他过程。 我已经在上面的其他项目中提到过这个,但是它应该有自己的编号。 为了使用多个内核 ,同时需要多个线程。 但相反的是不正确的。 单个核心不限于单个线程。 操作系统可以随时以较高的优先级过程(在不中断任何其他内核的情况下)预先中断某个进程(线程)的一个进程(线程),从而可能使时间测量偏差。 同样,较长的基准测试时间将有助于减少由此特定现象引起的线程对线程差异的影响。