Вопрос о параллельном цикле в MPI

#loops #parallel-processing #fortran #openmp #mpi

#циклы #параллельная обработка #fortran #openmp #mpi

Вопрос:

привет, у меня короткий вопрос об openmpi в fortran: у меня есть такой код:

 I) definitions of vars amp; linear code, setting up some vars for later usage
II) a while loop which works like that in pseudocode:

nr=1
while(true)
{
  filename='result'//nr//'.bin' (nr converted to string)
  if(!file_exists(filename))
    goto 100

  // file exists... so do something with it
  // calculations, read/write...
  nr=nr 1
}
100 continue
III) some more linear code...
  

Теперь я хочу сделать это параллельным вычислением с помощью openmpi. Линейный код из I) и III) должен быть вычислен только один раз, а циклы while должны выполняться на нескольких процессорах… Как это лучше всего реализовать?
моя проблема заключается в том, как работает цикл while: например, когда процессор 1 вычисляет result1.bin, как напрямую сообщить процессору 2 вычислить result2.bin? и как это будет работать, если есть 30 файлов, и я использую

mpirun -n 10 my_program

? как MPI «узнает», что после завершения вычисления одного файла есть еще файлы, «ожидающие» обработки: как только один процессор закончил обработку одного файла, ЭТОТ обработчик должен напрямую начать обработку следующего файла в очереди..

пока спасибо!

#

Редактировать:

#

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

 nfiles = 0
do
  write(filename,FMT='(A,I0,A)'), prefix, nfiles 1, suffix
  inquire(file=trim(filename),exist=exists)
  if (not(exists)) exit
    nfiles = nfiles   1
enddo
  

теперь я попробовал следующий код:

 call omp_set_num_threads(2)
!$OMP PARALLEL
!$OMP DO 
do i=startnum, endnum
  write(filename,FMT='(A,I0,A)'), prefix, i, suffix
  ...CODE DIRECTLY HERE TO PROCESS THE FILE...
enddo
!$OMP END DO
!$OMP END PARALLEL
  

Но это всегда выдает мне подобные ошибки:
«Незаконно выходить из цикла DO, связанного с открытой директивой MP DO или PARALLEL DO».

Всегда о кодовых линиях с такого рода кодом:

 read (F_RESULT,*,ERR=1) variable
  

Где F_RESULT — это дескриптор файла…Что в этом может быть не так?
переменная определяется вне блока цикла, и я уже пытался установить директиву OpenMP в

 private(variable) 
  

так что у каждого потока есть своя копия, но это не сработало!
Пока спасибо за вашу помощь!

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

1. Отредактировано для замены openmpi на mpi; это не относится к openmpi конкретно.

Ответ №1:

Вероятно, самый разумный способ сделать это — заставить один из процессов заранее подсчитать общее количество файлов, транслировать это, а затем попросить всех сделать «свои» файлы:

 program processfiles
    use mpi
    implicit none

    integer :: rank, comsize, ierr
    integer :: nfiles
    character(len=6) :: prefix="result"
    character(len=4) :: suffix=".bin"
    character(len=50) :: filename
    integer :: i
    integer :: locnumfiles, startnum, endnum
    logical :: exists

    call MPI_Init(ierr)
    call MPI_Comm_size(MPI_COMM_WORLD, comsize, ierr)
    call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr)

    ! rank zero finds number of files
    if (rank == 0) then
       nfiles = 0
       do
           write(filename,FMT='(A,I0,A)'), prefix, nfiles 1, suffix
           inquire(file=trim(filename),exist=exists)
           if (not(exists)) exit
           nfiles = nfiles   1
       enddo
    endif
    ! make sure everyone knows
    call MPI_Bcast(nfiles, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)

    if (nfiles /= 0) then
        ! calculate who gets what file
        locnumfiles = nfiles/comsize
        if (locnumfiles * comsize /= nfiles) locnumfiles = locnumfiles   1
        startnum = locnumfiles * rank   1
        endnum = startnum   locnumfiles - 1
        if (rank == comsize-1) endnum = nfiles
        do i=startnum, endnum
           write(filename,FMT='(A,I0,A)'), prefix, i, suffix
           call processfile(rank,filename)
        enddo
    else
        if (rank == 0) then
            print *,'No files found; exiting.'
        endif
    endif
    call MPI_Finalize(ierr)

    contains
        subroutine processfile(rank,filename)
            implicit none
            integer, intent(in) :: rank
            character(len=*), intent(in) :: filename
            integer :: unitno
            open(newunit=unitno, file=trim(filename))
            print '(I4,A,A)',rank,': Processing file ', filename
            close(unitno)
        end subroutine processfile
end program processfiles
  

А затем простой тест:

 $ seq 1 33 | xargs -I num touch "result"num".bin"
$ mpirun -np 2 ./processfiles

   0: Processing file result1.bin                                       
   0: Processing file result2.bin                                       
   0: Processing file result3.bin                                       
   0: Processing file result4.bin                                       
   0: Processing file result5.bin                                       
   0: Processing file result6.bin                                       
   1: Processing file result18.bin                                      
   0: Processing file result7.bin                                       
   0: Processing file result8.bin                                       
   1: Processing file result19.bin                                      
   0: Processing file result9.bin                                       
   1: Processing file result20.bin                                      
   0: Processing file result10.bin                                      
   1: Processing file result21.bin                                      
   1: Processing file result22.bin                                      
   0: Processing file result11.bin                                      
   1: Processing file result23.bin                                      
   0: Processing file result12.bin                                      
   1: Processing file result24.bin                                      
   1: Processing file result25.bin                                      
   0: Processing file result13.bin                                      
   0: Processing file result14.bin                                      
   1: Processing file result26.bin                                      
   1: Processing file result27.bin                                      
   0: Processing file result15.bin                                      
   0: Processing file result16.bin                                      
   1: Processing file result28.bin                                      
   1: Processing file result29.bin                                      
   1: Processing file result30.bin                                      
   0: Processing file result17.bin                                      
   1: Processing file result31.bin                                      
   1: Processing file result32.bin                                      
   1: Processing file result33.bin  
  

Обновлено, чтобы добавить дополнительный вопрос OpenMP:

Итак, в этом первом цикле вычисляется количество файлов перед началом параллельной обработки файлов. Этот подсчет файлов необходимо выполнить до того, как начнется параллельная обработка файлов, потому что в противном случае невозможно разделить работу между процессорами; вам нужно знать, сколько будет «рабочих единиц», прежде чем разделять работу. (Это не абсолютно единственный способ сделать что-то, но он самый простой).

Аналогично, OMP DO циклы требуют достаточно структурированных циклов — должен быть простой цикл, подобный do i=1,n , который затем можно легко разбить между потоками. n его не нужно компилировать, и приращение даже не обязательно должно быть единицей, но это должна быть такая вещь, которая может быть известна наверняка до фактического выполнения цикла. Так, например, вы не можете выйти из цикла из-за какой-либо внешней причины (например, отсутствия файла).)

Итак, что вы хотели бы сделать с OpenMP, так это выполнить тот же подсчет файлов и оставить это в покое, но затем в цикле обработки используйте конструкцию parallel do . Итак, после удаления содержимого MPI у вас будет что-то вроде:

     do
        write(filename,FMT='(A,I0,A)'), prefix, nfiles 1, suffix
        inquire(file=trim(filename),exist=exists)
        if (.not.exists) exit
        nfiles = nfiles   1
    enddo

    if (nfiles /= 0) then
        !$OMP PARALLEL SHARED(nfiles,prefix,suffix) PRIVATE(i,thread,filename)
        thread = omp_get_thread_num()
        !$OMP DO 
        do i=1, nfiles
           write(filename,FMT='(A,I0,A)'), prefix, i, suffix
           call processfile(thread,filename)
        enddo
        !$OMP END DO
        !$OMP END PARALLEL 
    else
        print *,'No files found; exiting.'
    endif
  

но все остальное было бы таким же. И снова, если вы хотите обрабатывать файлы «встроенно» (например, не в sburoutine), вам следует поместить код обработки файла туда, где находится строка ‘call processfile()’.

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

1. О, круто, это выглядит довольно круто… Единственная проблема заключается в том, что я с трудом могу разделить существующий код на «подпрограммы» из-за всех определенных переменных и т.д., Которые существуют и которые используются в цикле while (определены / объявлены перед циклом while)… Есть ли какой-нибудь «более простой» способ? Но, тем не менее, спасибо, я обязательно попробую это и рассмотрю возможность разделения моего существующего кода на части!

2. Ну, конечно, вы можете просто поместить обработку туда, где вызов подпрограммы идет выше. Однако в долгосрочной перспективе вы обнаружите, что ваше программное обеспечение намного проще обслуживать, если вы разбиваете его на функции и подпрограммы — гораздо проще видеть, что происходит.

3. Привет, отредактировал мой оригинальный пост, потому что я не могу разместить достаточно кода в комментарии здесь…

4. Пока спасибо. Как вы можете видеть, я уже «знал» это, поскольку я уже написал свой код, подобный приведенному выше… Мой вопрос относился к ошибке, которую я получаю при попытке скомпилировать код. Я просто не могу найти, что в этом плохого… РЕДАКТИРОВАТЬ: Ах, теперь у меня появилась идея по этому поводу: вероятно, «ERR = 1» неверно, поскольку ошибка приведет к выпадению кода из цикла, что недопустимо… Я попробую это без блока ERR = 1 🙂

5. Да, вы будете использовать максимум столько процессоров, сколько у вас потоков; Для OpenMP вам не нужно явно задавать количество потоков в коде; если вы ничего не сделаете, OpenMP автоматически будет использовать столько потоков, сколько у вас процессоров. Если вы хотите это изменить, вы можете установить переменную среды OMP_NUM_THREADS перед запуском программы, не изменяя ни строчки кода.