使用 task_group 的英特尔线程构建模块性能不佳(新用户)

Posted

技术标签:

【中文标题】使用 task_group 的英特尔线程构建模块性能不佳(新用户)【英文标题】:Poor performance with Intel Threading Building Blocks using task_group (new user) 【发布时间】:2018-08-29 11:50:49 【问题描述】:

我最近对英特尔线程构建模块产生了兴趣。我想利用tbb::task_group 类来管理线程池。

我的第一次尝试是构建一个将向量复制到另一个向量的测试:我创建了第 n 个任务,每个任务负责复制向量的连续切片。

但是,性能会随着线程数的增加而降低。我与另一个线程池实现有相同的结果。使用 TBB 2018 Update 5,gcc 6.3 on debian strecth on 8 i7 core box,我得到以下数字来复制 1'000'000 个元素的向量:

第n个真实用户

1 0.808s 0.807s

2 1.068s 2.105s

4 1.109s 4.282s

也许你们中的一些人会帮助我理解这个问题。代码如下:

#include<iostream>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include "tbb/task_group.h"
#include "tbb/task_scheduler_init.h"

namespace mgis
  using real = double;
  using size_type = size_t;


void my_copy(std::vector<mgis::real>& d,
         const std::vector<mgis::real>& s,
         const mgis::size_type b,
         const mgis::size_type e)
  const auto pb = s.begin()+b;
  const auto pe = s.begin()+e;
  const auto po = d.begin()+b;
  std::copy(pb,pe,po);


int main(const int argc, const char* const* argv) 
  using namespace mgis;
  if (argc != 3) 
    std::cerr << "invalid number of arguments\n";
    std::exit(-1);
  
  const auto ng = std::stoi(argv[1]);
  const auto nth = std::stoi(argv[2]);
  tbb::task_scheduler_init init(nth);
  tbb::task_group g;
  std::vector<real> v(ng,0);
  std::vector<real> v2(ng);
  for(auto i =0; i!=2000;++i)
    const auto d = ng / nth;
    const auto r = ng % nth;
    size_type b = 0;
    for (size_type i = 0; i != r; ++i) 
      g.run([&v2, &v, b, d]  my_copy(v2, v, b, b + d + 1); );
      b += d+1;
    
    for (size_type i = r; i != nth; ++i) 
      g.run([&v2, &v, b, d]  my_copy(v2, v, b, b + d); );
      b += d ;
    
    g.wait();
  
  return EXIT_SUCCESS;

【问题讨论】:

【参考方案1】:
    这么短的基准测试没有意义,因为 TBB 需要创建线程并启动它们,它不会在第一次调用 TBB 时立即发生,因为它是惰性异步进程。不过,您的用户时间表明线程已启动并正在运行,但可能没有工作要做。 memcopy 不利于可扩展性研究,因为它的扩展性不会超出内存控制器/通道的数量。因此,无论您拥有 4 个 CPU 还是 24 个 CPU,即使对于良好的硬件,您也不太可能获得超过 4 倍的加速。您的频道可能较少。 不要手动分割范围,使用tbb::parallel_for,你不需要task_group。而且,一个一个地调用任务具有线性复杂度,parallel_for 具有对数复杂度。

【讨论】:

感谢您的回复。 感谢您的回复。我的问题必须更清楚。为了澄清起见,我对这种类似 memcopy 的测试并不真正感兴趣,但对于更复杂的操作,基于任务的并行性更适合。但是,使用 openmp 进行的相同测试(用于指令的简单原始 #pragram 并行)确实显示随着线程数量的增加(据我所知,OpenMP 还必须启动它的池)线程)。尽管可扩展性很差,但我的观点是,随着 TBB 的性能下降,线程数会增加。 我将按照您的建议使用parallel_for 进行相同的测试,但是,正如我在之前的评论中所说,parallel_for 将不适合我的实际需求。 parallel_for 几乎总是有效。即使您没有有限数量的元素,即使它们的数量在恒定时间内不容易发现,但可以通过整除范围很好地表达。例如一棵二叉树,然后 parallel_for 可以很好地工作。如果您不想编写自定义范围类,使用paralle_do,您可以稍后添加其他项目threadingbuildingblocks.org/docs/help/reference/algorithms/…

以上是关于使用 task_group 的英特尔线程构建模块性能不佳(新用户)的主要内容,如果未能解决你的问题,请参考以下文章

线程池对英特尔线程构建块有用吗?

C++ 并行化库:OpenMP 与线程构建块 [关闭]

英特尔线程构建块并发队列:在 pop_if_present() 上使用 pop()

如何从命令行使用 VS2015 x64 构建 TBB?

具有140多个模块的英特尔Fortran 11.1长编译时间

英特尔自动矢量化行程计数解释?