atb*_*atb 7 c++ recursion multithreading tbb c++11
我试图使用tbb多线程现有的递归算法.单线程版本使用尾调用递归,从结构上看,它看起来像这样:
void my_func() {
my_recusive_func (0);
}
bool doSomeWork (int i, int& a, int& b, int& c) {
// do some work
}
void my_recusive_func (int i) {
int a, b, c;
bool notDone = doSomeWork (i, a, b, c);
if (notDone) {
my_recusive_func (a);
my_recusive_func (b);
my_recusive_func (c);
}
}
Run Code Online (Sandbox Code Playgroud)
我是tbb新手所以我的第一次尝试使用了parallel_invoke函数:
void my_recusive_func (int i) {
int a, b, c;
bool notDone = doSomeWork (i, a, b, c);
if (notDone) {
tbb::parallel_invoke (
[a]{my_recusive_func (a);},
[b]{my_recusive_func (b);},
[c]{my_recusive_func (c);});
}
}
Run Code Online (Sandbox Code Playgroud)
这确实有效,并且运行速度比单线程版本快,但它似乎不能很好地扩展核心数量.我所针对的机器有16个内核(32个超线程),因此可伸缩性对于这个项目来说非常重要,但是这个版本在该机器上最多只能获得8倍的加速,并且许多内核在算法运行时似乎处于空闲状态.
我的理论是tbb正在等待在parallel_invoke之后完成子任务,所以可能有许多任务闲置等待不必要?这会解释空闲核心吗?有没有办法让父任务返回而不等待孩子?我当时想的可能是这样的,但我对调度程序还不了解,但还不知道这是否正常:
void my_func()
{
tbb::task_group g;
my_recusive_func (0, g);
g.wait();
}
void my_recusive_func (int i, tbb::task_group& g) {
int a, b, c;
bool notDone = doSomeWork (i, a, b, c);
if (notDone) {
g.run([a,&g]{my_recusive_func(a, g);});
g.run([b,&g]{my_recusive_func(b, g);});
my_recusive_func (c, g);
}
}
Run Code Online (Sandbox Code Playgroud)
我的第一个问题是tbb::task_group::run()线程安全吗?我无法从文档中找到答案.此外,还有更好的方法来解决这个问题吗?也许我应该使用低级调度程序调用?
(我输入的代码没有编译,所以请原谅错别字.)
这里确实有两个问题:
通常最好从一个任务组中生成少量任务。如果使用递归并行性,请为每个级别指定其自己的任务组。尽管性能可能不会比使用parallel_invoke更好。
低级 tbb::task 接口是最好的选择。您甚至可以使用 tasK::execute 返回指向尾调用任务的指针的技巧来编写尾递归。
但我有点担心空闲线程。我想知道是否有足够的工作来保持线程忙碌。考虑首先进行工作跨度分析。如果您使用的是 Intel 编译器(或 gcc 4.9),您可以先尝试使用 Cilk 版本。如果这不能加快速度,那么即使是低级别的 tbb::task 接口也不太可能有帮助,并且需要检查更高级别的问题(工作和跨度)。