#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 перед запуском программы, не изменяя ни строчки кода.