omp ordered子句如何工作?

27
vector<int> v;

#pragma omp parallel for ordered schedule(dynamic, anyChunkSizeGreaterThan1)
    for (int i = 0; i < n; ++i){
            ...
            ...
            ...
#pragma omp ordered
            v.push_back(i);
    }

这会用大小为n的有序列表填充v

当到达omp ordered块时,所有线程都需要等待最低迭代可能的线程完成,但如果没有任何线程被指派到该特定迭代怎么办?或者OpenMP运行时库是否总是确保最低迭代由某个线程处理?

此外,为什么建议在dynamic scheduleordered子句一起使用?static schedule会影响性能吗?

1个回答

65

ordered 子句的工作方式如下:不同的线程同时执行,直到遇到 ordered 区域,然后按与串行循环中执行顺序相同的顺序顺序执行。这仍然允许一定程度的并发性,特别是在 ordered 区域以外的代码部分具有实质性的运行时间的情况下。

使用小块大小的 dynamic 调度而不是 static 调度没有特定的原因,这完全取决于代码的结构。由于 ordered 在线程之间引入了依赖关系,如果与默认块大小的 schedule(static) 一起使用,第二个线程将不得不等待第一个线程完成所有迭代,然后第三个线程将不得不等待第二个线程完成其迭代(因此也要等待第一个线程),依此类推。可以用 3 条线程和 9 次迭代(每个线程 3 次)轻松地进行可视化:

tid  List of     Timeline
     iterations
0    0,1,2       ==o==o==o
1    3,4,5       ==.......o==o==o
2    6,7,8       ==..............o==o==o

=表示线程在并行执行代码。o表示线程正在执行ordered区域。.表示线程处于空闲状态,等待执行ordered区域。使用schedule(static,1),将发生以下情况:

tid  List of     Timeline
     iterations
0    0,3,6       ==o==o==o
1    1,4,7       ==.o==o==o
2    2,5,8       ==..o==o==o

我相信两种情况的区别是非常明显的。使用 schedule(dynamic) 后,上面的图片将变得更加随机,因为分配给每个线程的迭代列表是不确定性的。这还会增加额外的开销。只有在每个迭代的计算量不同,并且计算所需时间比使用动态调度增加的开销要长得多时,才有用。

不要担心编号最小的迭代。通常由第一个准备执行代码的团队线程处理。


3
注意,@Cookie503,如果块大小太小,则由于丢失的数据局部性而可能使缓存变得不太有用。这可能会对性能造成比暗示的串行化更糟糕的影响。尝试使用不同的块大小,直到获得最佳的加速效果。明智地使用ordered循环,并尽可能避免它们,即使会导致增加内存占用 - 例如(针对您特定的示例),应该使用简单的预分配数组(或其他线程安全的随机访问容器)而不是vector:arr [i] = i; - Hristo Iliev
如果块大小太小,数据位于同一缓存行中,我们是否面临虚假共享的风险? - Wajdan Ali
@WajdanAli 这取决于数据类型及其排列方式,但通常情况下,使用较小的块比使用较大的块更容易遇到虚假共享问题。 - Hristo Iliev
你能否举个例子来说明如何使用这个语法? - Royi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接