Изменение переменной цикла (индекса) в OpenACC

У меня есть ситуация, когда мне нужно повторить определенную итерацию цикла несколько раз. Итак, в этой конкретной итерации я уменьшаю индекс на один шаг, чтобы следующее приращение индекса цикла не имело значения.

Этот подход, который я должен реализовать, работает для многопоточных кодов OpenMP. Однако это не работает для OpenACC (как для многоядерных целей, так и для целей tesla). Я получаю следующую ошибку:

Floating point exception (core dumped)

Вот код для обоих случаев:

#include <stdio.h>
#include <omp.h>
#include <unistd.h>

int main() {

    int x = 52;
    int count = 5;
    int i;

    omp_set_num_threads(6);

    #pragma omp parallel for
    for(i=0;i<100;i++) {
        if(i == x) {
            printf("%d\n", i);
            i--;
            count--;
            if(count == 0)
                x = 10000;
        }
    }


    int gpu_count = 0;
    count = 5;
    x = 52;

    #pragma acc parallel loop independent
    for(i=0;i<1000000;i++) {
        if(i == x) {
            #pragma acc atomic
            gpu_count++;

            i--;
            count--;
            if(count == 0)
                x = 2000000;
        }
    }
    printf("gpu_count: %d\n", gpu_count);


    return 0;
}

Для OpenMP я получаю правильный вывод:

52
52
52
52
52

Но для OpenACC я получаю вышеупомянутую ошибку.

Если я прокомментирую строку 35 (i--;), код будет выполнен правильно и выведет количество повторных итераций (которое равно 1).

Примечание. Я использую PGI 16.5 с Geforce GTX 970 и CUDA 7.5.

Я компилирую компилятором PGI следующим образом:

pgcc -mp -acc -ta=multicore -g f1.c

Итак, мой вопрос: почему я вижу такое поведение? Могу ли я изменить переменную индекса цикла в OpenACC?


person Millad    schedule 13.03.2017    source источник
comment
Я был бы очень удивлен, если бы это было разрешено. Вы не должны делать такие вещи в параллельных циклах вообще, не только в OpenACC (что я недостаточно хорошо знаю, чтобы ответить).   -  person Vladimir F    schedule 14.03.2017
comment
Если вам нужно что-то повторить внутри одной итерации, сделайте это другим способом. Например, внутри есть цикл while.   -  person Vladimir F    schedule 14.03.2017
comment
@VladimirF: спасибо за ваши правки. Я намеренно поместил OpenACC в начало, чтобы сделать его более удобным для всех пользователей. Что касается подхода while, который вы предложили, на самом деле, мое намерение для вышеуказанного подхода состояло в том, чтобы опустить внутренний цикл while. Вот почему я хотел сделать это, как указано выше.   -  person Millad    schedule 14.03.2017
comment
Не ставьте теги в начале вот так. Список тегов для этого. meta.stackexchange.com /вопросы/19190/   -  person Vladimir F    schedule 14.03.2017


Ответы (1)


Ваша версия OpenMP ошибочна. Вы полагаетесь на статическое расписание, в котором размер фрагмента больше, чем «количество». Если вы увеличите количество потоков OMP, чтобы размер фрагмента был меньше, чем количество, или если вы измените расписание для чередования фрагментов (например, «расписание (статическое, 1)»), вы получите неправильные ответы. Также есть условия гонки на «x» и «count».

Обратите внимание, что планирование OpenACC больше похоже на «статическое» OpenMP1, так что векторы могут получать доступ к непрерывным блокам памяти через воркер (он же деформация CUDA). Так что ваш алгоритм здесь тоже не сработает.

Кроме того, используя «независимое» предложение (которое подразумевается при использовании «параллельного цикла»), вы подтверждаете компилятору, что этот цикл не содержит зависимостей или что пользователь будет обрабатывать их с помощью директивы «atomic». Однако изменение переменной индекса цикла внутри тела цикла создаст зависимость цикла, поскольку значение индекса цикла зависит от того, изменила ли предыдущая итерация его значение.

Изменить: Ниже приведен пример, который представляет собой распараллеливаемую версию вашего кода.

% cat test2.c
#include <stdio.h>
#include <omp.h>
#include <unistd.h>

int main() {

    int x = 52;
    int count = 5;
    int i;
    int mycnt;

    #pragma omp parallel for schedule(static,1) private(mycnt)
    for(i=0;i<100;i++) {
        if(i == x) {
          mycnt = count;
          while(mycnt > 0) {
            printf("%d\n", i);
            mycnt--;
          }
        }
    }

#ifdef _OPENACC
    int gpu_count = 0;
    #pragma acc parallel loop reduction(+:gpu_count)
    for(i=0;i<1000000;i++) {
        if(i == x) {
          mycnt = count;
          while(mycnt > 0) {
            gpu_count++;
            mycnt--;
          }
        }
    }
    printf("gpu_count: %d\n", gpu_count);
#endif
    return 0;
}

% pgcc -fast -mp -acc test2.c  -Minfo=mp,acc
main:
     13, Parallel region activated
         Parallel loop activated with static cyclic schedule
     24, Barrier
         Parallel region terminated
     25, Accelerator kernel generated
         Generating Tesla code
         25, Generating reduction(+:gpu_count)
         26, #pragma acc loop gang, vector(128) /* blockIdx.x threadIdx.x */
         29, #pragma acc loop seq
     29, Loop carried scalar dependence for gpu_count at line 30
% a.out
52
52
52
52
52
gpu_count: 5
person Mat Colgrove    schedule 13.03.2017
comment
спасибо за указание размера куска. Кажется, что он будет печатать MIN (CHUNK_SIZE, MAX_COUNT) раз. У меня сложилось впечатление, что цикл for в OpenMP будет разбит на другие циклы for с меньшими границами. Однако, похоже, нет. По крайней мере, это то, что я видел в OpenUH. А что касается зависимости, я с вами не согласен, поскольку я пытаюсь снова запустить текущую итерацию и не полагаюсь на предыдущую итерацию. - person Millad; 14.03.2017
comment
Нет, это определенно зависимость. Параллельные циклы должны быть счетными. При изменении значения индекса цикла количество итераций цикла становится равным 100 + n, где n неизвестно в момент входа кода в цикл, что делает его неисчисляемым. - person Mat Colgrove; 14.03.2017
comment
Обратите внимание, что код не перезапускает текущую итерацию. Цикл переходит к следующей итерации, но индекс цикла уменьшается на единицу для некоторых итераций. Другими словами, если вы поместите счетчик в цикл, вы увидите, что он выполняется 105 раз, причем 5 из этих итераций имеют одно и то же значение для i. Однако до выполнения программы невозможно узнать, какие итерации имеют одинаковые значения i. Следовательно, цикл имеет зависимость, поскольку любая итерация не будет знать значение i до тех пор, пока не будет выполнена предыдущая итерация цикла. - person Mat Colgrove; 14.03.2017