Как заставить 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