Как заставить openMP выполнять итерации в определенном порядке
я использую openMP для параллельного запуска экземпляров моей симуляции.
#pragma omp parallel for private(part) shared(P,lfcc,temp)
for (part = 0; part < P->Parts; part++)
часть цикла проверяет, существует ли выходной файл с индексным номером "часть" (поэтому я могу запустить моделирование для определенного значения "части", а затем увеличить его без перезаписи существующих результатов). Однако для этого требуется, чтобы итерация выполнялась по порядку. то есть для n потоков он должен сначала одновременно запускать части (1)-(n), затем части (n+1)-(2n) и так далее. прямо сейчас (с 3-мя потоками, работающими параллельно, и "частями", установленными на N), поведение отличается, запуская первые части(0), (N/3), (2N/3), за которыми следуют(1), (N/3+1), (2N/3+1) и так далее.
предположим теперь, что я изначально хотел 30 частей, которые были завершены. Затем я решаю, что мне нужно больше деталей, и меняю "части" на 45. Затем первые нити получают детали (1)-(15), вторые (16)-(30) и третьи (31-45). Первые два потока быстро обнаруживают, что все их назначенные части уже были завершено и оставит последний поток работать в одиночку (если я поставлю предложение барьера до завершения программы).
одно простое решение-позволить переменной "часть" начать не с 0, а с m+1, где m-количество ранее завершенных частей. Но мне было интересно, можно ли заставить openMP запустить итерацию в порядке, показанном жирным шрифтом выше.
2 ответов
вы можете изменить размер блоков итерации, каждый поток получает 1
внутри schedule
предложения, например,schedule(static,1)
. С 3 потоками первый будет обрабатывать итерации 0, 3, 6, 9 и так далее, второй поток будет обрабатывать итерации 1, 4, 7, 10 и так далее, а третий будет обрабатывать итерации 2, 5, 8, 11 и так далее. Вам все равно нужно синхронизировать где-то в цикле, так как нет гарантии, что потоки будут выполнять все шаги одновременно и с одинаковой скоростью (вы может поставить барьер в конце каждой итерации для синхронизации до начала следующего блока итераций).
другое решение-использовать конструкцию задачи OpenMP. С его помощью вы можете запустить большой цикл в одном потоке, генерируя вычислительные задачи. Вы можете поместить проверки на наличие выходного файла внутри этого цикла и создавать новые задачи только при необходимости (например, выходной файл не существует):
#pragma omp parallel
{
...
#pragma omp single
for (part = 0; part < P->Parts; part++)
{
if (!output_file_exists(part))
#pragma omp task
{
... computation for that part ...
}
}
#pragma omp taskwait
...
}
надеюсь, я правильно понял вашу проблему.
если мы хотим, чтобы потоки OpenMP выполнялись в порядке, мы должны использовать ordered
предложения. Однако мы должны быть осторожны. Ниже будет напечатано i
(и идентификаторы потоков) в порядке (i
С 0
to 19
, tid от 0
to omp_get_num_threads() - 1
) :
#pragma omp parallel
#pragma omp for ordered
for (i = 0; i < 20; i++)
#pragma omp ordered
printf("i=%d - tid=%d\n", i, omp_get_thread_num());
выход (в моем 8-ядерном компьютере intel x86_64):
i=0 - tid=0
i=1 - tid=0
i=2 - tid=0
i=3 - tid=1
i=4 - tid=1
i=5 - tid=1
i=6 - tid=2
i=7 - tid=2
i=8 - tid=2
i=9 - tid=3
i=10 - tid=3
i=11 - tid=3
i=12 - tid=4
i=13 - tid=4
i=14 - tid=5
i=15 - tid=5
i=16 - tid=6
i=17 - tid=6
i=18 - tid=7
i=19 - tid=7
но обратите внимание:
#pragma omp parallel
#pragma omp for ordered
for (i = 0; i < 20; i++)
{
// the threads enter this for() section in order but won't
// print this statement in order!
printf("other i=%d - tid=%d\n", i, omp_get_thread_num());
#pragma omp ordered
// these are printed in order
printf("i=%d - tid=%d\n", i, omp_get_thread_num());
}
выход:
other i=16 - tid=6
other i=18 - tid=7
other i=12 - tid=4
other i=0 - tid=0
i=0 - tid=0
other i=1 - tid=0
i=1 - tid=0
other i=2 - tid=0
i=2 - tid=0
other i=3 - tid=1
other i=6 - tid=2
other i=14 - tid=5
i=3 - tid=1
other i=4 - tid=1
i=4 - tid=1
other i=5 - tid=1
i=5 - tid=1
i=6 - tid=2
other i=7 - tid=2
i=7 - tid=2
other i=8 - tid=2
i=8 - tid=2
other i=9 - tid=3
i=9 - tid=3
other i=10 - tid=3
i=10 - tid=3
other i=11 - tid=3
i=11 - tid=3
i=12 - tid=4
other i=13 - tid=4
i=13 - tid=4
i=14 - tid=5
other i=15 - tid=5
i=15 - tid=5
i=16 - tid=6
other i=17 - tid=6
i=17 - tid=6
i=18 - tid=7
other i=19 - tid=7
i=19 - tid=7
наконец, обратите внимание, что этот массив заполняется по порядку (я проверяю его позже при печати его последовательно из основного потока, чтобы убедиться).
// threads filling up array
int Arr[20] = {0};
#pragma omp parallel for ordered
for (i = 0; i < 20; i++)
Arr[i] = i;
printf("\n\n");
// lets check to see if threads have put values to the array in order
for (i = 0; i < 20; i++)
printf("Arr[%d]=%d\n", i, Arr[i]);
выход:
A[0]=0
A[1]=1
A[2]=2
A[3]=3
A[4]=4
A[5]=5
A[6]=6
A[7]=7
A[8]=8
A[9]=9
A[10]=10
A[11]=11
A[12]=12
A[13]=13
A[14]=14
A[15]=15
A[16]=16
A[17]=17
A[18]=18
A[19]=19