【问题标题】:Intel TBB vs Boost英特尔 TBB 与 Boost
【发布时间】:2011-10-31 02:18:30
【问题描述】:

在我的新应用程序中,我可以灵活地决定将库用于多线程。到目前为止,我使用的是 pthread。现在想探索跨平台库。我在 TBB 和 Boost 上归零。我不明白 TBB 比 Boost 有什么好处。 我试图找出 TBB 相对于 Boost 的优势: TBB Excerpts for wiki “相反,该库通过允许将操作视为“任务”来抽象对多个处理器的访问,这些操作由库的运行时引擎动态分配给各个内核,并通过自动有效地使用缓存。 TBB 程序根据算法创建、同步和销毁相关任务的图,”

但是线程库甚至需要担心线程分配给内核。这不是操作系统的工作吗? 那么使用 TBB 而不是 Boost 的真正好处是什么?

【问题讨论】:

  • 您也可以使用 pthread 设置线程亲和性(例如使用 pthread_setaffinity_np 调用)
  • @Foo 是的,没错。我的观点是这样做有多少实际用途。作为程序员,您可能不希望在您的应用程序中完成线程调度任务。那么为什么 TBB 将其显示为与其他库的区别呢?
  • 为应用程序正确选择内核具有明显的优势。以线程记录器的简单示例为例。一个线程从网络接口接收数据并将其放在环上;另一个线程从环中读取并写入文件(这有助于缓解使用 tcpdump 时看到的拥塞)。在这种情况下,在双处理器系统上,在同一个 CPU 上设置亲和性明显更有效。如果您启用了超线程,则使用虚拟核心对会快得多。但是,这需要大量的微观管理,而 TBB 不需要。
  • @Foo 感谢您的精彩解释。我同意在某些情况下您想要设置线程的亲和力。但是 TBB 将如何代表您(程序员)进行这种管理?换句话说,程序员必须直接TBB。在 boost/pthread 上也可以这样做!更大的问题是,当 TBB 比操作系统做更好的线程调度时,什么情况下?
  • @Foo.即使您使用 pthread_setaffinity_np() 正确设置线程的亲和性,也不能保证线程将以交错方式运行,尤其是在多道程序环境中。我仍然会说它可以给您一些好处,但有一个上限.

标签: boost boost-thread tbb


【解决方案1】:

但是线程库甚至需要担心线程分配给内核。这不是操作系统的工作吗?那么使用 TBB 而不是 Boost 的真正好处是什么?

你是对的,线程库通常不应该关心将线程映射到内核。而 TBB 没有。 TBB 使用任务而不是线程进行操作。 TBB 的调度程序通过分配线程池并让它动态选择要运行的任务来利用所有内核。这是与 Boost 相比的主要优势,您需要手动将可用工作映射到线程。然后 TBB 提供了高级构造,例如 parallel_for、parallel_pipeline 等,可用于表达最常见的并行模式,并隐藏所有与任务的操作。

例如,我们来看一段计算Mandelbrot分形点的代码(取自http://warp.povusers.org/Mandelbrot/,变量初始化省略):

for(unsigned y=0; y<ImageHeight; ++y)
{
    double c_im = MaxIm - y*Im_factor;
    for(unsigned x=0; x<ImageWidth; ++x)
    {
        double c_re = MinRe + x*Re_factor;

        double Z_re = c_re, Z_im = c_im;
        bool isInside = true;
        for(unsigned n=0; n<MaxIterations; ++n)
        {
            double Z_re2 = Z_re*Z_re, Z_im2 = Z_im*Z_im;
            if(Z_re2 + Z_im2 > 4)
            {
                isInside = false;
                break;
            }
            Z_im = 2*Z_re*Z_im + c_im;
            Z_re = Z_re2 - Z_im2 + c_re;
        }
        if(isInside) { putpixel(x, y); }
    }
}

现在要使其与 TBB 并行,您只需将最外层循环转换为 tbb::parallel_for(为简洁起见,我使用 C++11 lambda):

tbb::parallel_for(0, ImageHeight, [=](unsigned y)
{
    // the rest of code is exactly the same
    double c_im = MaxIm - y*Im_factor;
    for(unsigned x=0; x<ImageWidth; ++x)
    {
        ...
        // if putpixel() is not thread safe, a lock might be needed
        if(isInside) { putpixel(x, y); }
    }
});

TBB 会自动将所有循环迭代分配到可用内核上(您不必担心有多少)并动态平衡负载,这样如果某个线程有更多工作要做,其他线程不仅会等待它,还会提供帮助,最大限度地提高 CPU 利用率。尝试用原始线程来实现它,你会感觉到不同:)

【讨论】:

  • @Alexey 谢谢你的解释。现在我看到 tbb 看起来很有希望。你能推荐一些tbb的教程吗?
  • 你能告诉我[=] 它捕获了什么吗?
  • @user:lambda 函数使用来自调用上下文的任何内容。 [=] 告诉编译器它应该按值捕获它需要的一切。对于上面的代码,捕获集可以由MaxImIm_factorMinReRe_factorImageWidthMaxIterations 组成,除非其中任何一个是全局变量。
  • @AlexeyKukanov 全局变量或*this 的成员,我不得不指出。引用类成员只会导致捕获this 指针。当然,parallel_for 和只读访问可能不是问题。
【解决方案2】:

英特尔 TBB 引入了它自己的线程池/调度程序和执行模型(包括类似 parallel_for 构造的东西),而 Boost 只有基本的线程管理功能(就是这样创建线程和同步原语。)使用以下方法编写一个好的线程池Boost 是可能的,但很困难——TBB 已经带有一个高度优化的线程池。所以这完全取决于您的要求:如果您只需要“便携式 pthreads”,请使用 Boost,如果您需要更多,请使用 Intel TBB。

【讨论】:

  • 感谢信息 boost::threadpool 用于在 Boost 中处理线程池。所以它确实提供了一种拥有线程池的方法。我不知道的是,与 TBB 相比,它的效率/缺陷如何?
  • boost::threadpool 在很长一段时间内都处于非活动状态,到目前为止不如英特尔 TBB 强大。
  • @Anteru :我猜他的意思是boost::thread_group,但你是对的——Boost 的类只是一个线程容器,不像 TBB 那样提供内置调度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
  • 2018-06-13
  • 2011-10-10
相关资源
最近更新 更多