如何在cilk中跳出for循环?
How to break out of for loop in cilk?
for 循环如下所示:
cilk_for (int i=0; i<1000000; i++){
do something;
if(tag == 0){
break;
}
}
然后在编译时,我得到了这个错误:
error: break from parallel loop is not currently supported
您无法跳出 cilk_for
,因为 cilk_for
不理解迭代顺序。 Cilk Plus(以及 TBB 和 OpenMP 等)中并行循环的迭代可以乱序同时执行 and/or。除非程序可以预测未来,否则如果第 100 次迭代在第 50 次执行之前或同时运行,第 100 次迭代如何知道第 50 次迭代有中断?
如果您确实需要在开始迭代 i+1 之前在迭代 i 退出循环,那么您的算法本质上是顺序的,您不能使用 cilk_for
。但是,如果跳出循环是关于性能(做更少的工作)而不是正确性,那么您将遇到 class 个问题,称为 "speculative parallelism"。在推测并行中,您愿意为了并行的好处做一些额外的工作,但您尽量避免做太多额外的工作而失去并行的好处。
Cilk Plus 没有任何明确设计用于推测并行性的构造,但您可以相当轻松地编写一些代码。在这种情况下,最简单的事情是将 tag
变成循环外的原子变量并将条件更改为:
if (tag == 0)
continue;
您可以使用顺序一致的内存排序写入 tag
,但您可以选择使用宽松的内存排序来读取它以减少内存争用。专家领域通常会考虑宽松的内存排序,但在这种情况下,您的基础非常稳固。一个更复杂的系统将通过划分循环 space 并使用树结构在迭代中传播 "done" 标志来进一步减少内存争用。
请注意,如果您按照我上面的建议进行操作,那么 ALL 尚未完成的迭代将会看到变化,即使是那些按顺序会出现的迭代before 将 tag
设置为零的迭代。如果只想停止 后续 迭代,则不要更改 tag
,而是使用单独的原子 stop_i
变量,并将逻辑更改为:
atomic_int stop_i(1000000);
cilk_for (int i=0; i<1000000; i++) {
if (atomic_load(&stop_i, memory_order_relaxed) >= i)
continue;
do something;
if(tag == 0){
atomic_store(&stop_i, i, memory_order_seq_cst);
continue;
}
}
但是请注意,您仍然会在尝试的停止点之外进行许多迭代的推测执行。只有在您设置 stop_i
时尚未开始的迭代才会受到影响。
for 循环如下所示:
cilk_for (int i=0; i<1000000; i++){
do something;
if(tag == 0){
break;
}
}
然后在编译时,我得到了这个错误:
error: break from parallel loop is not currently supported
您无法跳出 cilk_for
,因为 cilk_for
不理解迭代顺序。 Cilk Plus(以及 TBB 和 OpenMP 等)中并行循环的迭代可以乱序同时执行 and/or。除非程序可以预测未来,否则如果第 100 次迭代在第 50 次执行之前或同时运行,第 100 次迭代如何知道第 50 次迭代有中断?
如果您确实需要在开始迭代 i+1 之前在迭代 i 退出循环,那么您的算法本质上是顺序的,您不能使用 cilk_for
。但是,如果跳出循环是关于性能(做更少的工作)而不是正确性,那么您将遇到 class 个问题,称为 "speculative parallelism"。在推测并行中,您愿意为了并行的好处做一些额外的工作,但您尽量避免做太多额外的工作而失去并行的好处。
Cilk Plus 没有任何明确设计用于推测并行性的构造,但您可以相当轻松地编写一些代码。在这种情况下,最简单的事情是将 tag
变成循环外的原子变量并将条件更改为:
if (tag == 0)
continue;
您可以使用顺序一致的内存排序写入 tag
,但您可以选择使用宽松的内存排序来读取它以减少内存争用。专家领域通常会考虑宽松的内存排序,但在这种情况下,您的基础非常稳固。一个更复杂的系统将通过划分循环 space 并使用树结构在迭代中传播 "done" 标志来进一步减少内存争用。
请注意,如果您按照我上面的建议进行操作,那么 ALL 尚未完成的迭代将会看到变化,即使是那些按顺序会出现的迭代before 将 tag
设置为零的迭代。如果只想停止 后续 迭代,则不要更改 tag
,而是使用单独的原子 stop_i
变量,并将逻辑更改为:
atomic_int stop_i(1000000);
cilk_for (int i=0; i<1000000; i++) {
if (atomic_load(&stop_i, memory_order_relaxed) >= i)
continue;
do something;
if(tag == 0){
atomic_store(&stop_i, i, memory_order_seq_cst);
continue;
}
}
但是请注意,您仍然会在尝试的停止点之外进行许多迭代的推测执行。只有在您设置 stop_i
时尚未开始的迭代才会受到影响。