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

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

I) definitions of vars & 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 моя_программа

? как 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, связанного с директивой Open MP DO или PARALLEL DO».

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

read (F_RESULT,*,ERR=1) variable

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

private(variable) 

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


person tim    schedule 10.04.2011    source источник
comment
Отредактировано для замены openmpi на mpi; это не специфично для openmpi.   -  person Jonathan Dursi    schedule 10.04.2011


Ответы (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, так это сделать тот же подсчет файлов и оставить его в покое, но затем в цикле processing использовать параллельную конструкцию 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

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

person Jonathan Dursi    schedule 10.04.2011
comment
О, круто, это выглядит довольно круто... Единственная проблема в том, что я с трудом могу разделить существующий код на подпрограммы из-за всех определенных переменных и т. д., которые существуют и которые используются в цикле while (определены/объявлены до -loop)... Есть ли способ проще? Но спасибо, я обязательно попробую и посмотрю, как разделить мой существующий код на части! - person tim; 10.04.2011
comment
Что ж, конечно, вы можете просто поместить обработку там, где выше идет вызов подпрограммы. Однако в долгосрочной перспективе вы обнаружите, что ваше программное обеспечение намного проще поддерживать, если вы разбиваете его на функции и подпрограммы — гораздо легче увидеть, что происходит. - person Jonathan Dursi; 10.04.2011
comment
Привет, отредактировал свой исходный пост, потому что я не могу разместить здесь достаточно кода в комментарии... - person tim; 11.04.2011
comment
Спасибо. Как видите, я уже знал это, так как я уже написал свой код, подобный приведенному выше... Мой вопрос относился к ошибке, которую я получаю, когда пытаюсь скомпилировать код. Я просто не могу найти, что в этом не так... РЕДАКТИРОВАТЬ: А, теперь у меня есть представление об этом: вероятно, ERR=1 неверен, так как ошибка заставит код выпрыгнуть из цикла, что не разрешено.. , попробую без блока ERR=1 :) - person tim; 11.04.2011
comment
Один короткий вопрос о omp_set_num_threads(2): Эквивалентен ли поток количеству используемых процессоров? Есть ли какой-нибудь простой способ просто использовать как можно больше процессоров или что было бы лучшим способом не жестко закодировать это в самом файле?! Может быть, передать это как аргумент программе? :) - person tim; 11.04.2011
comment
Да, вы будете использовать не больше процессоров, чем потоков; Для OpenMP вам не нужно явно задавать количество потоков в коде; если вы ничего не делаете, OpenMP автоматически использует столько потоков, сколько у вас есть процессоров. Если вы хотите изменить это, вы можете установить переменную среды OMP_NUM_THREADS перед запуском программы, не меняя ни строчки кода. - person Jonathan Dursi; 11.04.2011