Запуск потока для каждого внутреннего цикла в OpenMP
-
19-09-2019 - |
Вопрос
Я довольно новичок в OpenMP, и я пытаюсь запустить отдельный поток для обработки каждого элемента в 2D-массиве.
Итак, по сути, это:
for (i = 0; i < dimension; i++) {
for (int j = 0; j < dimension; j++) {
a[i][j] = b[i][j] + c[i][j];
То, что я делаю, это:
#pragma omp parallel for shared(a,b,c) private(i,j) reduction(+:diff) schedule(dynamic)
for (i = 0; i < dimension; i++) {
for (int j = 0; j < dimension; j++) {
a[i][j] = b[i][j] + c[i][j];
Действительно ли это запускает поток для каждого 2D-элемента или нет?Как бы я это проверил?Если это неправильно, то каков правильный способ это сделать?Спасибо!
Примечание:Код был значительно упрощен
Решение
В вашем примере кода параллелен только внешний цикл.Вы можете протестировать, распечатав omp_get_thread_num()
во внутреннем цикле, и вы увидите, что для данного i
, номер потока один и тот же (конечно, этот тест является демонстративным, а не окончательным, поскольку разные запуски будут давать разные результаты).Например, с помощью:
#include <stdio.h>
#include <omp.h>
#define dimension 4
int main() {
#pragma omp parallel for
for (int i = 0; i < dimension; i++)
for (int j = 0; j < dimension; j++)
printf("i=%d, j=%d, thread = %d\n", i, j, omp_get_thread_num());
}
Я получаю:
i=1, j=0, thread = 1
i=3, j=0, thread = 3
i=2, j=0, thread = 2
i=0, j=0, thread = 0
i=1, j=1, thread = 1
i=3, j=1, thread = 3
i=2, j=1, thread = 2
i=0, j=1, thread = 0
i=1, j=2, thread = 1
i=3, j=2, thread = 3
i=2, j=2, thread = 2
i=0, j=2, thread = 0
i=1, j=3, thread = 1
i=3, j=3, thread = 3
i=2, j=3, thread = 2
i=0, j=3, thread = 0
Что касается остальной части вашего кода, возможно, вы захотите добавить больше деталей в новый вопрос (это трудно определить по небольшому образцу), но, например, вы не можете поместить private(j)
когда j
объявляется только позже.В моем примере выше он автоматически становится приватным.Я думаю diff
это переменная, которую мы не видим в выборке.Кроме того, переменная цикла i
автоматически становится приватным (из спецификация версии 2.5 - то же самое в спецификации 3.0)
Переменная итерации цикла в for-цикле for или параллельной for конструкции является частной в этой конструкции.
Редактировать:Все вышесказанное верно для кода, который мы с вами показали, но вас может заинтересовать следующее.Для OpenMP версии 3.0 (доступно, например, в gcc версия 4.4, но не версии 4.3) существует collapse
предложение, в котором вы могли бы написать код так, как у вас есть, но с
#pragma omp parallel for collapse (2)
чтобы распараллелить оба цикла for (см. спецификация).
Редактировать:Хорошо, я скачал gcc 4.5.0 и запустил приведенный выше код, но используя collapse (2)
чтобы получить следующий вывод, показывающий, что внутренний цикл теперь распараллелен:
i=0, j=0, thread = 0
i=0, j=2, thread = 1
i=1, j=0, thread = 2
i=2, j=0, thread = 4
i=0, j=1, thread = 0
i=1, j=2, thread = 3
i=3, j=0, thread = 6
i=2, j=2, thread = 5
i=3, j=2, thread = 7
i=0, j=3, thread = 1
i=1, j=1, thread = 2
i=2, j=1, thread = 4
i=1, j=3, thread = 3
i=3, j=1, thread = 6
i=2, j=3, thread = 5
i=3, j=3, thread = 7
Комментарии здесь (поиск "Обходные пути") также актуальны для обходных путей в версии 2.5, если вы хотите распараллелить оба цикла, но спецификация версии 2.5, приведенная выше, довольно четкая (см. несоответствующие примеры в разделе A.35).
Другие советы
Вы можете попробовать использовать вложенные omp-параллельные fors (после omp_set_nested(1)
вызов), но они поддерживаются не во всех реализациях openmp.
Итак, я предполагаю создать некоторую 2D-сетку и запустить весь поток на сетке из одного for (пример для фиксированной сетки потоков 4x4):
#pragma omp parallel for
for(k = 0; k < 16; k++)
{
int i,j,i_min,j_min,i_max,j_max;
i_min=(k/4) * (dimension/4);
i_max=(k/4 + 1) * (dimension/4);
j_min=(k%4) * (dimension/4);
j_max=(k%4 + 1) * (dimension/4);
for(i=i_min;i<i_max;i++)
for(j=j_min;j<j_max;j++)
f(i,j);
}