Опубликован: 22.04.2008 | Уровень: специалист | Доступ: платный | ВУЗ: Лаборатория Параллельных информационных технологий НИВЦ МГУ
Лекция 4:

Коллективные взаимодействия процессов

< Лекция 3 || Лекция 4: 123 || Лекция 5 >
MPI_ALLTOALLV(SBUF, SCOUNTS, SDISPLS, STYPE, RBUF, RCOUNTS,
RDISPLS, RTYPE, COMM, IERR)
<type> SBUF(*), RBUF(*)
INTEGER SCOUNTS(*), SDISPLS(*), STYPE, RCOUNTS(*), RDISPLS(*),
RTYPE, COMM, IERR

Рассылка со всех процессов коммуникатора сомм различного количества данных всем процессам данного коммуникатора. Размещение данных в буфере SBUF отсылающего процесса определяется массивом SDISPLS, а размещение данных в буфере RBUF принимающего процесса определяется массивом RDISPLS.

MPI_REDUCE(SBUF, RBUF, COUNT, DATATYPE, OP, ROOT, COMM, IERR)
<type> SBUF(*), RBUF(*)
INTEGER COUNT, DATATYPE, OP, ROOT, COMM, IERR

Выполнение COUNT независимых глобальных операций OP над соответствующими элементами массивов SBUF. Результат выполнения операции ОР над 1-ми элементами массивов SBUF всех процессов коммуникатора сомм получается в 1-ом элементе массива RBUF процесса ROOT.

В MPI предусмотрен ряд предопределенных глобальных операций, они задаются следующими константами:

  • MPI_MAX, MPI_MIN - определение максимального и минимального значения;
  • MPI_MINLOC, MPI_MAXLOC - определение максимального и минимального значения и их местоположения;
  • MPI_SUM, MPI_PROD - вычисление глобальной суммы и глобального произведения;
  • MPI_LAND, MPI_LOR, MPI_LXOR - логические "И", "ИЛИ", исключающее "ИЛИ";
  • MPI_BAND, MPI_BOR, MPI_BXOR - побитовые "И", "ИЛИ", исключающее "ИЛИ".

Кроме того, программист может задать свою функцию для выполнения глобальной операции при помощи процедуры MPI_OP_CREATE.

В следующем примере операция глобального суммирования моделируется при помощи схемы сдваивания с использованием пересылок данных типа точка-точка. Эффективность такого моделирования сравнивается с использованием коллективной операции MPI_REDUCE.

program example14
include  'mpif.h'
integer ierr, rank, i, size, n, nproc
parameter (n = 1 000 000)
double precision time_start, time_finish
double precision a(n), b(n), c(n)
integer status(MPI_STATUS_SIZE)
call MPI_INIT(ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
nproc = size
do i = 1, n
a(i) = 1.d0/size end do
call MPI_BARRIER(MPI_COMM_WORLD, ierr) time_start = MPI_WTIME(ierr) do i = 1, n
c(i) = a(i) end do do while (nproc .gt. 1)
if(rank .lt. nproc/2) then
call MPI_RECV(b, n, MPI_DOUBLE_PRECISION,
& nproc-rank-1, 1, MPI_COMM_WORLD,
& status, ierr)
do i = 1, n
c(i) = c(i) + b(i) end do else if(rank .lt. nproc) then
call MPI_SEND(c, n, MPI_DOUBLE_PRECISION,
& nproc-rank-1, 1, MPI_COMM_WORLD, ierr)
end if
nproc = nproc/2 end do do i = 1, n
b(i) = c(i) end do
time_finish = MPI_WTIME(ierr)-time_start if(rank .eq. 0) print *, 
 'model b(1)=', b(1) print *, 'rank=', rank, ' model time =', time_finish
do i = 1, n
a(i) = 1.d0/size end do call MPI_BARRIER(MPI_COMM_WORLD, ierr)
time_start = MPI_WTIME(ierr)
call MPI_REDUCE(a, b, n, MPI_DOUBLE_PRECISION, MPI_SUM, 0,
& MPI_COMM_WORLD, ierr)
time_finish = MPI_WTIME(ierr)-time_start if(rank .eq. 0) print *, 
 'reduce b(1)=', b(1) print *, 'rank=', rank,
 ' reduce time =', time_finish call MPI_FINALIZE(ierr) end
MPI_ALLREDUCE(SBUF, RBUF, COUNT, DATATYPE, OP, COMM, IERR)
<type> SBUF(*), RBUF(*)
INTEGER COUNT, DATATYPE, OP, COMM, IERR
Листинг 4.2.

Выполнение COUNT независимых глобальных операций ОР над соответствующими элементами массивов SBUF. Отличие от процедуры MPI_REDUCE В том, что результат получается в массиве RBUF каждого процесса.

В следующем примере каждый процесс вычисляет построчные суммы элементов локального массива а, после чего полученные суммы со всех процессов складываются при помощи процедуры MPI_ALLREDUCE, И результат получается в массиве г на всех процессах приложения.

do i = 1, n
s(i) = 0.0 end do do i = 1, n
do j = 1, m
s(i)  = s(i)+a(i,  j)
end do end do
call MPI_ALLREDUCE(s, r, n, MPI_REAL, MPI_SUM,
& MPI_COMM_WORLD, IERR)
MPI_REDUCE_SCATTER(SBUF, RBUF, RCOUNTS, DATATYPE, OP, COMM,IERR)
<type> SBUF(*), RBUF(*)
INTEGER RCOUNTS(*), DATATYPE, OP, COMM, IERR

Выполнение RCOUNTS (i) независимых глобальных операций ОР над соответствующими элементами массивов SBUF. Функционально это эквивалентно тому, что сначала выполняются глобальные операции, затем результат рассылается по процессам, i-ый процесс получает ( i+D -ую порцию результатов из RCOUNTS (i+1) элементов и помещает в массив RBUF. Массив RCOUNTS должен быть одинаковым на всех процессах коммуникатора сомм.

MPI_SCAN(SBUF, RBUF, COUNT, DATATYPE, OP, COMM, IERR)
<type> SBUF(*), RBUF(*)
INTEGER COUNT, DATATYPE, OP, COMM, IERR

Выполнение COUNT независимых частичных глобальных операций ОР над соответствующими элементами массивов SBUF. i-ЫЙ процесс выполняет COUNT глобальных операций над соответствующими элементами массива SBUF процессов с номерами от о до I включительно и помещает полученный результат в массив RBUF. Полный результат глобальной операции получается в массиве RBUF последнего процесса.

MPI_OP_CREATE(FUNC, COMMUTE, OP, IERR) EXTERNAL FUNC LOGICAL COMMUTE INTEGER OP, IERR

Создание пользовательской глобальной операции ОР, которая будет вычисляться функцией FUNC. Создаваемая операция должна быть ассоциативной, а если параметр COMMUTE равен . TRUE., то она должна быть также и коммутативной. Если параметр COMMUTE равен .FALSE., TO порядок выполнения глобальной операции строго фиксируется согласно увеличению номеров процессов, начиная с процесса с номером о.

FUNCTION FUNC(INVEC(*), INOUTVEC(*), LEN, TYPE) 
 <type> INVEC(LEN), INOUTVEC(LEN) INTEGER LEN, TYPE

Таким образом задается интерфейс пользовательской функции для создания глобальной операции. Первый аргумент операции берется из параметра INVEC, второй аргумент - из параметра INOUTVEC, а результат возвращается в параметре INOUTVEC. Параметр LEN задает количество элементов входного и выходного массивов, а параметр TYPE - тип входных и выходных данных. В пользовательской функции не должны производиться никакие обмены данными с использованием вызовов процедур MPI.

MPI_OP_FREE(OP, IERR) INTEGER OP, IERR

Уничтожение пользовательской глобальной операции. По выполнении процедуры переменной ОР присваивается значение MPI_OP_NULL.

Следующий пример демонстрирует задание пользовательской функции для использования в качестве глобальной операции. Задается функция smod5, вычисляющая поэлементную сумму по модулю 5 векторов целочисленных аргументов. Данная функция объявляется в качестве глобальной операции ор в вызове процедуры MPI_OP_CREATE, затем используется в процедуре MPI_REDUCE, после чего удаляется с помощью вызова процедуры

MPI_OP_FREE.
program example15
include 'mpif.h'
integer ierr, rank, i, n
parameter (n = 1 000)
integer a(n), b(n)
integer op
external smod5
call MPI_INIT(ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
do i = 1, n
a(i) = i + rank end do
print *, 'process ', rank, ' a(1) =', a(1)
call MPI_OP_CREATE(smod5, .TRUE., op, ierr)
call MPI_REDUCE(a, b, n, MPI_INTEGER, op, 0,
& MPI_COMM_WORLD, ierr)
call MPI_OP_FREE(op, ierr) if(rank .eq. 0) print *,
  ' b(1) =', b(1) call MPI_FINALIZE(ierr) end
integer function smod5(in, inout, l, type)
integer l, type
integer in(l), inout(l), i
do i = 1, l
inout(i) = mod(in(i)+inout(i), 5) end do return end

Задания

  • Чем коллективные операции отличаются от взаимодействий типа точка-точка?
  • Верно ли, что в коллективных взаимодействиях участвуют все процессы приложения?
  • Могут ли возникать конфликты между обычными сообщениями, посылаемыми процессами друг другу, и сообщениями коллективных операций? Если да, как они разрешаются?
  • Можно ли при помощи процедуры MPI_RECV принять сообщение, посланное процедурой MPI_BCAST?
  • Смоделировать барьерную синхронизацию при помощи пересылок точка-точка и сравнить эффективность такой реализации и стандартной процедуры MPI_BARRIER.
  • В чем различие в функциональности процедур MPI_BCAST И MPI_SCATTER?
  • Смоделировать глобальное суммирование методом сдваивания и сравнить эффективность такой реализации с использованием стандартной процедуры MPI_REDUCE.
  • Смоделировать процедуру MPI_ALLREDUCE при помощи процедур MPI_REDUCE И MPI_BCAST.
  • Напишите свой вариант процедуры MPI_GATHER, используя функции посылки сообщений типа точка-точка.
  • Подумайте, как организовать коллективный асинхронный обмен данными, аналогичный функции: a) MPI_REDUCE ; б) MPI_ALLTOALL.
  • Исследовать масштабируемость (зависимость времени выполнения от числа процессов) различных коллективных операций на конкретной системе.
< Лекция 3 || Лекция 4: 123 || Лекция 5 >
Артем Калашник
Артем Калашник
Украина
Анастасия Ёлкина
Анастасия Ёлкина
Украина