Параллельный цикл выполнения Openmp работает корректно ~ 50% времени

#fortran #openmp

#fortran #openmp

Вопрос:

В настоящее время я работаю над добавлением распараллеливания openmp для цикла выполнения в одном из кодов, которые я написал для исследования. Я довольно новичок в использовании openmp, поэтому был бы признателен, если бы у вас были какие-либо предложения о том, что может пойти не так.

В принципе, я добавил параллельный цикл выполнения в следующий код (который работает до распараллеливания). r(:,:,:,:) — это вектор тонны молекулярных координат, индексированных по времени, молекуле, атому и (xyz). Этот вектор составляет около 100 ГБ данных (я работаю на HPC с большим количеством оперативной памяти). Я пытаюсь распараллелить внешний цикл и разделить его между процессорами, чтобы сократить время, затрачиваемое на вычисления. Я подумал, что было бы неплохо сделать это с помощью msd и cm_msd — это единственные вещи, которые нужно будет редактировать несколькими процессорами и сохранять на потом, что, поскольку каждая итерация получает свой собственный элемент этих массивов, у них не будет состояния гонки.

Проблема: если я запускаю этот код 5 раз, я получаю разные результаты, иногда msd вычисляется правильно (или кажется), а иногда он выводит все нули позже, когда я усредняю их вместе. Без распараллеливания проблем нет.

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

 ! Loop over time origins
    counti = 0
    ind = 0
    !$OMP PARALLEL DO schedule(static) PRIVATE(i,j,k,it,r_old,r_cm_old,shift,shift_cm,dsq,ind) amp;
    !$OMPamp; SHARED(msd,msd_cm)
    do i=1, nconfigs-nt, or_int
        if(MOD(counti*or_int,500) == 0) then
            write(*,*) 'Reached the ', counti*or_int,'th time origin'
        end if
        ! Set the Old Coordinates
        counti = counti   1
        ind = (i-1)/or_int   1
        r_old(:,:,:) = r(i,:,:,:)
        r_cm_old(:,:) = r_cm(i,:,:)
        shift = 0.0
        shift_cm = 0.0

        ! Loop over the timesteps in each trajectory
        do it=i 2, nt i
            ! Loop over molecules
            do j = 1, nmols
                do k=1, atms_per_mol
                    ! Calculate the shift if it occurs.
                    shift(j,k,:) = shift(j,k,:) - L(:)*anint((r(it,j,k,:) - amp;
                        r_old(j,k,:) )/L(:))
                    ! Calculate the square displacements
                    dsq = ( r(it,j,k,1)   shift(j,k,1) - r(i,j,k,1) ) ** 2. amp;
                          ( r(it,j,k,2)   shift(j,k,2) - r(i,j,k,2) ) ** 2. amp;
                          ( r(it,j,k,3)   shift(j,k,3) - r(i,j,k,3) ) ** 2.
                    msd(ind, it-1-i, k) = msd(ind, it-1-i, k)   dsq
                    ! Calculate the contribution to the c1,c2
                enddo ! End Atoms Loop (k)
                ! Calculate the shift if it occurs.
                shift_cm(j,:) = shift_cm(j,:) - L(:)*anint((r_cm(it,j,:) - amp;
                                r_cm_old(j,:) )/L(:))
                ! Calculate the square displacements
                dsq = ( r_cm(it,j,1)   shift_cm(j,1) - r_cm(i,j,1) ) ** 2. amp;
                     ( r_cm(it,j,2)   shift_cm(j,2) - r_cm(i,j,2) ) ** 2. amp;
                     ( r_cm(it,j,3)   shift_cm(j,3) - r_cm(i,j,3) ) ** 2.
                msd_cm(ind,it-1-i) = msd_cm(ind, it-1-i)   dsq
            enddo ! End Molecules Loop (j)
            r_old(:,:,:) = r(it,:,:,:)
            r_cm_old(:,:) = r_cm(it,:,:)
       enddo ! End t's loop (it)
    enddo
    !$OMP END PARALLEL DO
 

При запуске этого кода, когда я позже печатаю усредненные результаты msd, они либо получаются корректными, либо равными нулю, и это всегда одно или другое. Видите ли вы проблему, которая может объяснить, почему она работает только часть времени. Я новичок в openmp, поэтому вполне возможно, что в том, как я пытаюсь это сделать, есть что-то невероятно глупое.

Заранее спасибо!

Комментарии:

1. Выгружать сюда целую кучу кода и спрашивать: «Почему это не работает» — не лучший способ получить помощь. Пожалуйста, сократите его до минимального примера. Когда код иногда работает, а иногда нет, это часто является условием гонки, особенно при параллельном программировании. В этом случае я бы заподозрил строки, в которых значения присваиваются общим массивам msd и msd_cm не защищаются директивой critical or atomic .

2. Да, это очень похоже на состояние гонки. Я подозреваю, что изменение области видимости msd и msd_cm с общего на уменьшение более исправит это, но без полной программы, демонстрирующей проблему, невозможно проверить и сказать наверняка.

3. Спасибо за отзыв на мой вопрос, это первый раз, когда я публикую на SO, и поэтому я не понял, что недостаточно соединил его. Это действительно было условие гонки с участием msd и msd_cm, которое я смог решить с помощью сокращения и добавления директивы по умолчанию (none), чтобы заставить себя идентифицировать все общие / частные переменные. Еще раз спасибо!