非常基本的循环使用TBB

时间:2011-12-29 05:16:43

标签: c++ multithreading tbb

我是一名非常新的程序员,我在使用intel的例子时遇到了一些麻烦。我认为如果能看到tbb中最基本的可能循环是如何实现的话会有所帮助。

for (n=0 ; n < songinfo.frames; ++n) {  

         sli[n]=songin[n*2];
         sri[n]=songin[n*2+1];

}

这是我用来解交错音频数据的循环。这个循环会从tbb中受益吗?你会如何实现它?

1 个答案:

答案 0 :(得分:6)

首先,对于以下代码,我假设您的arrays属于mytype*类型,否则代码需要进行一些修改。此外,我假设您的范围不重叠,否则并行化尝试将无法正常工作(至少没有更多的工作)

因为你在tbb中提出要求:

首先,您需要在某处初始化库(通常在main中)。对于代码假设我在某处放置了using namespace tbb

int main(int argc, char *argv[]){
   task_scheduler_init init;
   ...
}

然后你需要一个函子捕获你的数组并执行forloop的主体:

struct apply_func {
    const mytype* songin; //whatever type you are operating on
    mytype* sli;
    mytype* sri;
    apply_func(const mytype* sin, mytype* sl, mytype* sr):songin(sin), sli(sl), sri(sr)
    {}
    void operator()(const blocked_range<size_t>& range) {
      for(size_t n = range.begin(); n !=range.end(); ++n){
        sli[n]=songin[n*2];
        sri[n]=songin[n*2+1];
      }
    }
}

现在您可以使用parallel_for并行化此循环:

size_t grainsize = 1000; //or whatever you decide on (testing required for best performance);
apply_func func(songin, sli, sri);
parallel_for(blocked_range<size_t>(0, songinfo.frames, grainsize), func);

应该这样做(如果我没记错的话,有一段时间没有看过tbb,所以可能会有小错误)。 如果您使用c ++ 11,则可以使用lambda

来简化代码
size_t grainsize = 1000; //or whatever you decide on (testing required for best performance);
parallel_for(blocked_range<size_t>(0, songinfo.frames, grainsize), 
             [&](const blocked_range<size_t>&){
                for(size_t n = range.begin(); n !=range.end(); ++n){
                  sli[n]=songin[n*2];
                  sri[n]=songin[n*2+1];
                }
             });

据说Tbb并不是我推荐给新程序员的。我真的建议只对并行化的代码进行并行处理,直到你对线程有一个非常牢固的控制。为此,我建议使用openmp,这比使用tbb更安静一点,但仍然足够强大并行化很多东西(取决于编译器支持它)。对于你的循环,它将如下所示:

#pragma omp prallel for
for(size_t n = 0; n < songinfo.frames; ++n) {
  sli[n]=songin[n*2];
  sri[n]=songin[n*2+1];
}

然后你必须告诉你的编译器使用openmp进行编译和链接(-fopenmp用于gcc,/openmp用于visual c ++)。你可以看到它使用起来相当简单(对于这种简单的使用方法,更复杂的风格是另一回事)然后tbb并且还具有不支持openmp或tbb的workonform的额外好处(因为未知{{ 1}}被编译器忽略。我个人在某些项目中使用openmp支持tbb,因为我无法使用它的开源许可证而购买tbb对项目来说有点陡峭。

现在我们已经知道了如何将循环平行化,让我们回答这个问题是否值得。这是一个真正无法轻易回答的问题,因为它完全取决于您处理的元素数量以及您的程序预期运行的平台类型。你的问题带宽非常大,所以我不会指望性能提升很多。

  • 如果您只处理#pragmas元素,则由于开销,循环的并行版本很可能比单线程版本慢。
  • 如果您的数据不在缓存中(因为它不适合)并且您的系统缺乏带宽,您可能看不到很多好处(尽管您可能会看到一些好处,但不要如果它的大小为1000,即使你使用了很多处理器,也会受到侮辱。
  • 如果您的系统是ccNUMA(可能用于多插槽系统),由于额外的转移成本,无论元素数量多少,您的性能都可能会下降
  • 编译器可能会错过有关指针别名的优化(因为循环体移动到不同的节点)。使用1.X(对于gcc,对于vs没有任何线索)可能有助于解决这个问题。
  • ...

就我个人而言,我认为您最有可能看到显着性能提升的情况是,您的系统是否具有单个多核cpu,数据集适合于L3-Cache(但不适用于单独的L2缓存)。对于更大的数据集,您的性能可能会提高,但不会太多(并且正确使用预取可能会获得类似的收益)。当然这是纯粹的推测。