Вперед: 1.4.2.2. Коммуникационный интерфейс MPI
Назад: 1.4.2. Программирование для систем с распределенной памятью
К содержанию: Оглавление


1.4.2.1. Коммуникационный интерфейс PVM

Библиотека PVM (Parallel Virtual Machine) является одной из первых систем программирования, базирующейся на механизме передачи сообщений и получившей широкое распространение. Она проектировалась для того, чтобы связать отдельные независимые компьютеры в виртуальную вычислительную систему, которая являлась бы единым управляемым вычислительным ресурсом. Библиотека PVM ориентирована на работу с гетерогенными системами с распределенной памятью. Первая версия PVM была создана в 1989 в Окриджской национальной лаборатории, но она не была опубликована. Вторая версия была заново написана в 1991 г. в университете Ноксвилла (штат Теннеси) [13]. Текущая версия 3.4.6 выпущена в 2009 г. и доступна для свободного скачивания и использования. Имеются реализации для всех основных операционных систем, и поддерживается широкий набор языков программирования - Fortran, С/C++, Tcl/Tk, Perl, Piton.

Система PVM состоит из двух частей: PVM сервера (pvmd) и пользовательских библиотек (libpvm3.a libfpvm3.a). Сервер pvmd обеспечивает коммуникации между компьютерами и управление процессами. На каждом компьютере виртуальной параллельной машины запускается один pvmd сервер. Первый pvmd сервер, запускаемый пользователем, становится мастер-процессом, в то время как все другие pvmd процессы, запускаемые мастером, называются рабочими. PVM библиотеки позволяют задаче пользователя взаимодействовать с pvmd серверами на других узлах. Они содержат функции для упаковки и распаковки сообщений и функции для передачи сообщений.

Приведем пример головной и рабочей программ на языке Фортран.

Программа pi_master.f

program pimaster
   include '/usr/share/pvm3/include/fpvm3.h'
   integer n, i
   double precision d, s, pi
   integer mytid,numprocs,tids(0:32),status
   integer numt,msgtype,info
   character*8 arch
   write(*,*) 'n, numprocs?'
   read(*,*) n, numprocs
   call PVMFMYTID(mytid)
   arch = '*'
   call PVMFSPAWN('piworker',PVMDEFAULT,arch,
   $        numprocs,tids,numt)
   if( numt .lt. numprocs) then
     write(*,*) 'trouble spawning'
     call PVMFEXIT(info)
     stop
   endif
   d = 1.0/n
   msgtype = 0
   do 10 i=0, numprocs-1
    call PVMFINITSEND(PVMDEFAULT,info)
    call PVMFPACK(INTEGER4, numprocs, 1, 1, info)
    call PVMFPACK(INTEGER4, i,    1, 1, info)
    call PVMFPACK(INTEGER4, n,    1, 1, info)
    call PVMFPACK(REAL8,  d,    1, 1, info)
    call PVMFSEND(tids(i),msgtype,info)
 10  continue
   s=0.0
   write(*,*) 'n, numprocs?'
   read(*,*) n, numprocs
   call PVMFMYTID(mytid)
   arch = '*'
   call PVMFSPAWN('piworker',PVMDEFAULT,arch,
   $        numprocs,tids,numt)
   if( numt .lt. numprocs) then
     write(*,*) 'trouble spawning'
     call PVMFEXIT(info)
     stop
   endif
   d = 1.0/n
   msgtype = 0
   do 10 i=0, numprocs-1
    call PVMFINITSEND(PVMDEFAULT,info)
    call PVMFPACK(INTEGER4, numprocs, 1, 1, info)
    call PVMFPACK(INTEGER4, i,    1, 1, info)
    call PVMFPACK(INTEGER4, n,    1, 1, info)
    call PVMFPACK(REAL8,  d,    1, 1, info)
    call PVMFSEND(tids(i),msgtype,info)
 10  continue
   s=0.0
   msgtype = 5
   do 20 i=0, numprocs-1
     call PVMFRECV(-1,msgtype,info)
     call PVMFUNPACK(REAL8,x,1,1,info)
     s = s+x
 20  continue
   pi = d*s
   write(*,100) pi
 100 format(' pi = ', f20.15)
   call PVMFEXIT(info)
   end

Программа pi_worker.f

program piworker
   include '/usr/share/pvm3/include/fpvm3.h'
   integer n, i
   double precision s, x, d
   integer mytid,myid,numprocs,msgtype,master,info
   call PVMFMYTID(mytid)
   msgtype = 0
   call PVMFRECV(-1,msgtype,info)
   call PVMFUNPACK(INTEGER4, numprocs, 1, 1, info)
   call PVMFUNPACK(INTEGER4, myid,   1, 1, info)
   call PVMFUNPACK(INTEGER4, n,    1, 1, info)
   call PVMFUNPACK(REAL8,  d,    1, 1, info)
   s = 0.0
   do 10 i = myid+1, n, numprocs
    x = (i-0.5)*d
    s = s+4.0/(1.0+x*x)
 10  continue
   call PVMFINITSEND(PVMDEFAULT,info)
   call PVMFPACK(REAL8, s,1,1, info)
   call PVMFPARENT(master)
   msgtype = 5
   call PVMFSEND(master,msgtype,info)
   call PVMFEXIT(info)
   end

Программа pi_master.f является головной программой, а программа pi_worker.f - рабочей или вычислительной программой. Они должны находиться в разных файлах, компилироваться отдельно, и имя исполнимого файла рабочей программы должно быть в нашем случае piworker. Обе программы включают заголовочный файл fpvm3.h.

Первый вызов в основной программе PVM функции PVMFMYTID() сообщает серверу pvmd о запуске PVM программы и возвращает идентификатор PVM процесса. После того, как программа зарегистрирована в виртуальной машине, головная программа порождает рабочие процессы с помощью процедуры PVMFSPAWN (). В данной процедуре первым аргументом является символьная строка, содержащая название исполнимого файла, запускаемого в рабочих процессах. Четвертый аргумент определяет число копий рабочей программы и пятый аргумент - массив целого типа, который содержит идентификаторы всех запущенных задач. Последний аргумент возвращает число успешно запущенных задач.

При посылке сообщения от одной задачи к другой внутри PVM нужно выполнить как минимум 3 действия: инициализировать буфер для передаваемого сообщения, упаковать передаваемые данные в соответствии с форматами их представления и переслать сообщение в буфер получателя.

При приеме сообщения, во-первых, нужно определить алгоритм приема (блокированный или неблокированный), затем, приняв сообщение, его нужно распаковать, т.е. восстановить переданные фрагменты данных в соответствии с их типами. При посылке сообщения ему присваивается идентификатор, принимающий целые неотрицательные значения. В то же время, в отличие от задач разные сообщения могут иметь одинаковые идентификаторы, поскольку есть гарантированная возможность различать их по другим признакам (tid отправителя или получателя, время прихода и т.д. ).

Процедура PVMFINITSEND () создает и очищает буфер и возвращает идентификатор буфера. Заполнение буфера выполняется процедурой PVMFPACK (). В один буфер могут упаковываться данные разного типа. Важно чтобы полученное сообщение было распаковано таким же образом, как оно было упаковано перед передачей. Распаковка выполняется процедурой PVMFUNPACK (). Передача данных выполняется процедурой PVMFSEND (), которая вместе с данными передает идентификатор сообщения. Адресат, которому передается сообщение, определяется первым аргументом процедуры.

По окончании вычислений головная программа получает частичные суммы от каждой рабочей программы с помощью процедуры PVMFRECV(). Эта процедура получает сообщение от задачи, определяемой первым аргументом. Второй аргумент определяет идентификатор сообщения. Если первый или второй аргумент равны <-1>, то это означает возможность получения сообщений от любого отправителя или с любым идентификатором.

После завершения вычислений, головная программа сообщает серверу PVM, что работа завершена и виртуальная машина освобождается. Для этого вызывается подпрограмма PVMFEXIT (). Головная и рабочие программы используют одни и те же процедуры, кроме PVMFPARENT (), которую используют рабочие программы для получения идентификатора головной программы.

При компиляции PVM программ необходимо подключение PVM библиотек. Исполнимые файлы рабочих программ должны находиться в специальных каталогах на рабочих узлах. На все узлы должен быть разрешен беспарольный доступ по протоколу rsh.

Неоспоримым плюсом PVM является предоставляемая каждому пользователю возможность самостоятельно конфигурировать виртуальную машину и использовать для вычислений гетерогенные сети, однако система PVM плохо интегрируется с диспетчерскими системами, автоматизирующие выполнение потока разнообразных заданий. С этой точки зрения значительно более удобной системой является MPI. Кроме того, MPI значительно лучше поддерживается на специализированных скоростных сетях (Infiniband, InfiniPath, QsNET).



Вперед: 1.4.2.2. Коммуникационный интерфейс MPI
Назад: 1.4.2. Программирование для систем с распределенной памятью
К содержанию: Оглавление