使用TBB的非常基本的for循环

Very Basic for loop using TBB

本文关键字:for 循环 非常 TBB 使用      更新时间:2023-10-16

我是一个非常新的程序员,我有一些麻烦从英特尔的例子。如果我能看到最基本的循环是如何在tbb中实现的,我想这将是有帮助的。

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

这是一个循环,我正在使用去交错音频数据。这个循环会受益于它吗?你将如何实现它?

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

既然你在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);

应该可以了(如果我没记错的话,我有一段时间没看了,所以可能会有一些小错误)。如果使用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];
                }
             });

话虽如此,这并不是我推荐给新程序员的东西。我真的建议只并行化那些在你非常牢固地掌握线程之前不需要并行化的代码。对于这一点,我建议使用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的平台上工作的额外好处(因为未知的#pragmas被编译器忽略)。就我个人而言,我在一些项目中使用openmp而不是tbb,因为我不能使用它的开源许可证,而且购买tbb对项目来说有点贵。

现在我们已经知道了如何并行化循环,让我们来看看是否值得这样做。这是一个很难回答的问题,因为这完全取决于你要处理多少元素,以及你的程序要运行在什么样的平台上。你的问题是带宽非常大,所以我不指望性能有很大的提高。

  • 如果您只处理1000元素,则由于开销的原因,并行版本的循环很可能比单线程版本慢。
  • 如果你的数据不在缓存中(因为它不适合),并且你的系统非常缺乏带宽,你可能看不到太多的好处(尽管很可能你会看到一些好处,只是不要惊讶,如果它在1.X的顺序,即使你使用了很多处理器)
  • 如果您的系统是ccNUMA(可能用于多套接字系统),由于额外的传输成本,无论元素的数量如何,性能都可能下降
  • 编译器可能会错过关于指针混叠的优化(因为循环体被移动到不同的函数中)。使用__restrict (gcc, vs没有线索)可能有助于解决这个问题。

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