Украина |
Коллективные взаимодействия процессов
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.
- Исследовать масштабируемость (зависимость времени выполнения от числа процессов) различных коллективных операций на конкретной системе.