Редактировать
Давайте повторим все. Я внедряю Bellman-Ford на OpenMP. Насколько я понимаю, шаг 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;
}
}
}
Следует иметь в виду, что есть способ позволить критическому разделу делать только критические вещи, а затем приступить к установке локально-потокового значения bool, а затем уменьшить локальные значения в конце каждой итерации. Как мне этого добиться?