如何在 OpenMP 中减少 for 循环内关键部分中的值?
How to reduce a value inside a critical section inside a for loop in OpenMP?
>Edit
让我们重申一切。我正在OpenMP上实现Bellman-Ford。据我了解,compare
步骤和dist
设置必须在关键块中完成,因为更新dist
可能会改变compare
步骤的结果 - 这里有数据竞赛。
那么我的问题是updated_last_round
变量不需要在关键块中更新。这里有一个数据竞赛,但唯一的更新值是true
,所以没关系。我对当前实现的担忧是所有线程都在原子更新updated_last_round
并相互减慢速度。
bool compare(int v1, int w, int v2) {
// checks if v1 + w < v2
if (v1 == INT_MAX) return false;
if (v2 == INT_MAX) return true;
return ((v1+w) < v2);
}
vector<int> bellman_ford(Graph& g, int root) {
vector<int> dist(g.num_nodes());
# pragma omp parallel for
for (int i = 0; i < g.num_nodes(); i++)
dist[i] = INT_MAX; // set INF
dist[root] = 0;
int round = 0;
bool updated_last_round = true;
// relax procedure
while (updated_last_round && round < g.num_nodes()) {
updated_last_round = false;
#pragma omp parallel for
for (int u = 0; u < g.num_nodes(); u++) {
vector<int> neighbors = g.out_neighbors(u);
vector<int> weights = g.out_weights_neighbors(u);
#pragma omp parallel for
for (int j = 0; j < g.out_degree(u); j++) {
int v = neighbors[j];
int weight = weights[j];
#pragma omp critical
{
if (compare(dist[u], weight, dist[v])) {
dist[v] = dist[u] + weight;
updated_last_round = updated_last_round || true;
}
}
}
}
round += 1;
}
/* ... */
return dist;
}
源语言
我正在尝试并行化 OpenMP 中的一些代码,这些代码需要在并行 for 循环中进行原子检查和设置,并且我在每次迭代结束时计算是否至少设置了一个值。
现在我正在使用reduction(||:updated_last_round)
在每次迭代结束时减少布尔值,但我不确定这是否真的加快了速度,因为更新布尔值的实际代码行仍然在关键部分内。
bool updated_last_round = true
while (updated_last_round) {
updated_last_round = false;
#pragma omp parallel for reduction(||:updated_last_round)
for (/* stuff */) {
// other stuff
#pragma omp critical
{
if (should_update(vars_to_decide_with)) {
// do the important critical update
// I DON'T want this to be atomically updated, as
// the data race doesn't matter at all
updated_last_round = updated_last_round || true;
}
}
}
有一种方法可以让关键部分只做关键的事情,然后继续设置线程本地布尔值,然后在每次迭代结束时减少局部值,这应该是有意义的。我应该如何实现这一目标?
首先,同时写入updated_last_round
在技术上仍然是一个竞争条件,即使您只写入相同的值也是如此。
但是,不必担心写入updated_last_round
。与关键部分的总体开销相比,这不太可能重要。请担心每个微小的内部循环迭代中关键部分的开销。鉴于对dist[v]
和dist[u]
的读写依赖,我认为没有任何方法可以解决关键部分。
如何添加缩减并仍然在关键部分中设置updated_last_round
。从理论上讲,这将加快这种写入速度,因为它现在是局部的,而不是具有缓存失效的共享变量。但同样,与关键部分的巨大开销相比,这并不重要。
注意:从并行化中获得的唯一好处是out_*neighbors
函数非常昂贵。但我假设它们只返回一个固定的向量 - 出于性能原因,您应该返回并按const&
捕获。
如果要有效地并行化此算法,则必须考虑以某种方式对数据进行分区以解决依赖关系。小心:不幸的是,搜索">Bellman-Ford OpenMP"节目给出了一些非常不正确的尝试,例如SO上这个投票和接受的答案。
除此之外,不要使用嵌套并行性(parallel
parallel
内部,除非你真的知道你在做什么)。并行化最外层的循环,这是安全的,如果它带来性能优势,则使用collapse
在尽可能局部地声明变量方面也做得很好 - 这使得推理竞争条件变得更加容易。对于矢量副本来说,这可能有点棘手 - 无论如何都应该const&
。
有一种方法可以让关键部分 只做关键的事情,然后继续设置线程本地布尔值 值,然后在每次迭代结束时减小局部值。如何 我应该实现吗?
像这样的东西? 在我看来,这是您刚才描述的明显实现。 我已将测试移到关键部分之外;如果没有更多信息,目前尚不清楚这是否安全......
bool updated_last_round = true
while (updated_last_round) {
updated_last_round = false;
#pragma omp parallel for reduction(||:updated_last_round)
for (/* stuff */) {
// other stuff
bool updated_this_iteration = false;
if (should_update(vars_to_decide_with))
{
#pragma omp critical
{
// do the important critical update
}
// Set local, per-iteration, value
updated_this_iteration = true;
}
updated_last_round = updated_last_round || updated_this_iteration;
}
}
- "error: no matching function for call to"构造函数错误
- 表示"accepting anything for this template argument" C++概念的通配符
- 如何在C++中从两个单独的for循环中添加两个数组
- 在Linux for Windows上编译C++代码时出错
- 使用一个考虑到std::map中键值的滚动或换行的键
- 调用专用模板时出错"no matching function for call to [...]"
- 为什么我的for循环不能正确获取argv
- 为什么我不能在 FOR LOOP 中使用 i/10,C++?
- Arduino:for/while/if在void setup()或void loop()之前?——错误:之前需要不合格
- 如何在c++中只将键插入到bimap的一侧
- 在基于范围的for循环中使用结构化绑定声明
- 使用2个键的cpp-stl::优先级队列排序不正确
- 有效地使用std::unordered_map来插入或增加键的值
- 通过for循环使用用户输入填充列表
- 使用for循环检查数组中的重复项
- 在for循环中使用auto vs decltype(vec.size())来处理字符串的向量
- 如何创建Google+Plus一键,同时已经使用GooglePlay游戏服务SDK for C++实现了排行榜
- 是否可以在C++中使用命名变量(例如,键和值)而不是 .first 和 .second 进行 std::map<> "for element : container" 迭代?
- 如果没有for循环,我如何直接找到映射中指向两个不同键的任何两个迭代器之间的元素数量
- c++中,通过使用for循环在map中存储键和值