Мне нужно перебрать массив и назначить каждый элемент в соответствии с вычислением, которое требует некоторой итерации. Удаляя все ненужные детали, программа сводится к следующему.
float output[n];
const float input[n] = ...;
for (int i = 0; i < n; ++i) {
output[i] = 0.0f;
for (int j = 0; j < n; ++j) {
output[i] += some_calculation(i, input[j]);
}
}
some_calculation
не изменяет свои аргументы и не имеет внутреннего состояния, поэтому его потокобезопасен. Глядя на циклы, я понимаю, что внешний цикл является потокобезопасным, потому что разные итерации выводят в разные области памяти (разные output[i]
), а общие элементы input
никогда не изменяются во время выполнения цикла, но внутренний цикл не потокобезопасен, потому что имеет условие гонки на output[i]
, потому что он изменяется на всех итерациях.
Следовательно, я бы хотел порождать потоки и заставлять их работать для разных значений i
, но вся итерация по input
должна быть локальной для каждого потока, чтобы не вводить условие гонки в output[i]
. Я думаю, что следующее достигает этого.
std::array<float, n> output;
const std::array<float, n> input[n];
#pragma omp parallel for
for (int i = 0; i < n; ++i) {
output[i] = 0.0f;
for (int j = 0; j < n; ++j) {
output[i] += some_calculation(i, input[j]);
}
}
Я не уверен, как это обрабатывает внутренний цикл. Потоки, работающие на разных i
, должны иметь возможность выполнять цикл параллельно, но я не понимаю, позволяю ли я им без другой директивы #pragma omp
. С другой стороны, я не хочу, чтобы случайно запускать потоки для разных значений j
по сравнению с тем же i
, потому что это приводит к состоянию гонки. Я также не уверен, что мне нужна дополнительная спецификация о том, как следует обрабатывать два массива.
Наконец, если этот код находится в функции, которая будет вызываться повторно, нужна ли ей директива parallel
или она может быть вызвана один раз, прежде чем мой основной цикл начнется так.
void iterative_step(const std::array<float, n> &input, const std::array<float, n> &output) {
// Threads have already been spawned
#pragma omp for
for (int i = 0; i < n; ++i) {
output[i] = 0.0f;
for (int j = 0; j < n; ++j) {
output[i] += some_calculation(i, input[j]);
}
}
int main() {
...
// spawn threads once, but not for this loop
#pragma omp parallel
while (...) {
iterative_step(input, output);
}
...
}
Я просматривал различные другие вопросы, но они касались разных проблем с разными условиями гонки, и я не совсем понимаю, как обобщать ответы.