我正在研究TBB中的任务实现,并运行了Fibonacci系列的并行和连续计算代码。
守则是:
#include <iostream>
#include <list>
#include <tbb/task.h>
#include <tbb/task_group.h>
#include <stdlib.h>
#include "tbb/compat/thread"
#include "tbb/task_scheduler_init.h"
using namespace std;
using namespace tbb;
#define CutOff 2
long serialFib( long n ) {
if( n<2 )
return n;
else
return serialFib(n-1) + serialFib(n-2);
}
class FibTask: public task
{
public:
const long n;
long* const sum;
FibTask( long n_, long* sum_ ) : n(n_), sum(sum_) {}
task* execute()
{
// cout<<"task id of thread is \t"<<this_thread::get_id()<<"FibTask(n)="<<n<<endl; // Overrides virtual function task::execute
// cout<<"Task Stolen is"<<is_stolen_task()<<endl;
if( n<CutOff )
{
*sum = serialFib(n);
}
else
{
long x, y;
FibTask& a = *new( allocate_child() ) FibTask(n-1,&x);
FibTask& b = *new( allocate_child() ) FibTask(n-2,&y);
set_ref_count(3); // 3 = 2 children + 1 for wait // ref_countis used to keep track of the number of tasks spawned at the current level of the task graph
spawn( b );
// cout<<"child id of thread is \t"<<this_thread::get_id()<<"calculating n ="<<n<<endl;
spawn_and_wait_for_all( a ); //set tasks for execution and wait for them
*sum = x+y;
}
return NULL;
}
};
long parallelFib( long n )
{
long sum;
FibTask& a = *new(task::allocate_root()) FibTask(n,&sum);
task::spawn_root_and_wait(a);
return sum;
}
int main()
{
long i,j;
cout<<fixed;
cout<<"Fibonacci Series parallelly formed is "<<endl;
tick_count t0=tick_count::now();
for(i=0;i<50;i++)
cout<<parallelFib(i)<<"\t";
// cout<<"parallel execution of Fibonacci series for n=10 \t"<<parallelFib(i)<<endl;
tick_count t1=tick_count::now();
double t=(t1-t0).seconds();
cout<<"Time Elapsed in Parallel Execution is \t"<<t<<endl;
cout<<"\n Fibonacci Series Serially formed is "<<endl;
tick_count t3=tick_count::now();
for(j=0;j<50;j++)
cout<<serialFib(j)<<"\t";
tick_count t4=tick_count::now();
double t5=(t4-t3).seconds();
cout<<"Time Elapsed in Serial Execution is \t"<<t5<<endl;
return(0);
}
并行执行与串行执行相比花费更多时间。在此并行执行需要2500秒,而串行需要大约167秒。 任何人都可以解释一下这个原因吗?
答案 0 :(得分:6)
开销。
当您的实际任务是轻量级时,协调/通信占主导地位,您不会(自动)从并行执行中获益。这是一个非常常见的问题。
尝试连续计算M Fibonacci数(足够高的成本),然后并行计算它们。你应该看到收益。
答案 1 :(得分:2)
将Cutoff更改为12,使用优化进行编译(Linux上为-O; Windows上为/ O2),您应该会看到显着的加速。
示例中有很多并行性。问题是,当Cutoff = 2时,有用并行计算的各个单元被调度开销淹没。提高截止值应解决问题。
这是分析。分析并行性有两个重要时刻:
可用的并行性是工作/跨度。
对于fib(n),当n足够大时,工作大致与fib(n)成比例[是的,它描述了自己!]。跨度是调用树的深度 - 它大致与n成比例。因此,并行性与fib(n)/ n成比例。因此,即使n = 10,也有足够的可用并行性来保持典型的2013桌面机器的嗡嗡声。
问题是TBB任务需要时间来创建,执行,同步和销毁。将Cutoff从2更改为12允许串行代码在工作量太小以至于调度开销会使其淹没时接管。这是递归并行中的一种常见模式:并行递归,直到你完成大部分可能连续完成的工作。在其他并行框架(如OpenMP或Cilk Plus)中存在相同的问题:任务有开销,尽管它们可能比TBB更多或更少。所有这些变化都是最佳阈值。
尝试改变截止值。较低的值应该为您提供更多并行性但更多的调度开销。值越高,并行性越少,但调度开销越少。在这两者之间,您可能会发现一系列值可以提供更好的加速。
答案 2 :(得分:0)
如果没有更多信息,将很难说清楚。您需要检查:您的计算机有多少个processros?有没有其他可能使用过处理器的程序? 如果您希望以(真)并行运行并获得性能优势,那么操作系统必须能够分配至少2个免费处理器。 此外,对于小任务,分配线程和收集结果的开销 可能会超出并行执行的好处。
答案 3 :(得分:0)
我是否正确地认为每个任务都有result of fib(n-1) + result of fib(n-2)
- 所以基本上,你开始一个任务,然后开始另一个任务,等等,直到我们有大量的任务(我有点失去了尝试统计他们 - 我认为它是平方的)。并且每个这样的任务的结果用于累加斐波纳契数。
首先,这里没有实际的并行执行(除了两个独立的递归计算之外)。每个任务都依赖于它的子任务的结果,并且不能真正并行执行任何操作。另一方面,您正在执行大量工作来设置每项任务。毫不奇怪你没有看到任何好处)
现在,如果您通过迭代计算斐波纳契数1 ... 50,并且您开始,比如说,系统中每个处理器核心一个任务,并将其与仅使用单个循环的迭代解决方案进行比较,我'我相信这会有更好的改善。