【发布时间】:2015-05-30 08:30:21
【问题描述】:
在学习 cilk 时,我举了两个相反的例子:
相反的是那两条线:
x = spawn fib (n-1);
y = spawn fib (n-2);
第一个网站说:
你不需要给第二个递归添加 cilk_spawn 属性 调用
fib(),因为这将创建一个空的延续
- 我不明白为什么?
- 什么是正确的方法? (使用 2 个 spawn 命令还是只使用一个?)
【问题讨论】:
在学习 cilk 时,我举了两个相反的例子:
相反的是那两条线:
x = spawn fib (n-1);
y = spawn fib (n-2);
第一个网站说:
你不需要给第二个递归添加 cilk_spawn 属性 调用
fib(),因为这将创建一个空的延续
【问题讨论】:
英特尔文档“int x = cilk_spawn fib(n-1);”中的代码请求允许在另一个威胁中运行它,而下一行“int y = fib(n-2);”将在与主程序相同的威胁下运行。 wiki 代码还要求计算 fib(n-2) 的新威胁,以便如果有超过三个处理器来运行主程序 (fib(n))、fib (n-1) 和 fib(n-2 ) 都针对单独的威胁。
如果只有两个处理器,这两个代码的执行方式相同。以下来自维基页面:
...处理器没有义务在别处分配衍生过程; 如果机器只有两个处理器并且当执行 fib(2) 的处理器进入过程调用时,第二个处理器仍然忙于 fib(1),第一个处理器将挂起 fib(2 ) 并执行 fib(0) 本身,就像它是唯一的处理器一样。当然,如果另一个处理器可用,那么它将被调用,并且所有三个处理器将同时执行单独的帧。
【讨论】:
您所指的 Wikipedia 中的示例来自 MIT Cilk,它要求生成对 Cilk 函数的所有调用。 Cilk++ 取消了这一要求,并演变为英特尔的 Cilk Plus 实施。
请记住,被盗的是延续,而不是生成的函数。在派生函数之前执行代码的工作人员将执行派生函数。 cilk_spawn将 continuation 推送到 worker 的双端队列上,使其可供空闲的 worker 窃取。
考虑在 Cilk Plus 中完整实现 fib:
int fib(int n) {
if (n < 2)
return n;
int x = cilk_spawn fib(n-1);
int y = fib(n-2);
cilk_sync;
return x+y;
}
如果您在对 fib 的第二次递归调用中添加了 cilk_spawn,那么您将允许空闲的工作人员在该调用之后窃取延续。但那条链立即在cilk_sync 结束,所以这是浪费时间。
【讨论】: