Опубликован: 11.10.2012 | Уровень: специалист | Доступ: платный

Самостоятельная работа 2: Знакомство с процедурами буферизованного и неблокирующего двухточечного обмена MPI

Двухточечный буферизованный обмен

При передаче сообщения в буферизованном режиме источник копирует сообщение в буфер, а затем передает его в неблокирующем режиме. Выделение буфера и его размер контролируются программистом, который должен заранее создать буфер достаточного размера. Буферизованная передача завершается сразу, поскольку сообщение немедленно копируется в буфер для последующей передачи.

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

Буферизованный обмен рекомендуется использовать в тех ситуациях, когда программисту требуется больший контроль над распределением памяти.

Двухточечные неблокирующие обмены

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

Неблокирующий обмен выполняется в два этапа:

  1. Инициализация обмена.
  2. Проверка завершения обмена.

Для маркировки неблокирующих операций обмена используются идентификаторы операции обмена.

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

Подпрограмма MPI_Wait блокирует работу процесса до завершения приема или передачи сообщения. Функции MPI_Wait и MPI_Test можно использовать для завершения операций приема и передачи.

Подпрограммы-пробники

Получить информацию о сообщении до его помещения в буфер приема можно с помощью подпрограмм-пробников MPI_Probe и MPI_IProbe. На основании полученной информации принимается решение о дальнейших действиях. С помощью вызова подпрограммы MPI_Probe фиксируется поступление (но не прием!) сообщения. Затем определяется источник сообщения, его длина, выделяется буфер подходящего размера и выполняется прием сообщения.

Лабораторная работа

В заданиях лабораторной работы 2 предлагается дописать предлагаемые фрагменты программ на языках Fortran и C, написанные с использованием процедур MPI. Пропущенные фрагменты обозначены многоточием.

Задание 1

В исходном тексте программы на языке C пропущены вызовы процедур буферизованного обмена. Добавить эти вызовы, откомпилировать и запустить программу.

#include "mpi.h"
#include <stdio.h>
int main(int argc,char *argv[])
{
int *buffer;
int myrank;
MPI_Status status;
int buffsize = 1;
int TAG = 0;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
if (myrank == 0)
{
buffer = (int *) malloc(buffsize + MPI_BSEND_OVERHEAD);
buffer = (int *) 10;
}
else
{
MPI_Recv(&buffer, buffsize, MPI_INT, 0, TAG, MPI_COMM_WORLD,
&status);
printf("received: %i\n", buffer);
}
MPI_Finalize();
return 0;
}

Задание 2

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

program main_mpi
include 'mpif.h'
integer rank, tag, cnt, ierr, status(mpi_status_size)
integer request
real sndbuf(5) /1., 2., 3., 4., 5./
real rcvbuf(5)
cnt = 5
tag = 0
call mpi_init(ierr)
call mpi_comm_rank(mpi_comm_world, rank, ierr)
if(rank.eq.0) then
print *, "process ", rank, " send before wait", sndbuf
print *, "process ", rank, " send after wait", sndbuf
else
print *, "process ", rank, " received before wait", rcvbuf
print *, "process ", rank, " received after wait", rcvbuf
end if
call mpi_finalize(ierr)
stop
end

Задание 3

В исходном тексте программы на языке C пропущены вызовы подпрограмм-пробников. Добавить эти вызовы, откомпилировать и запустить программу.

#include "mpi.h"
#include <stdio.h>
int main(int argc,char *argv[])
{
int myid, numprocs, **buf, source, i;
int message[3] = {0, 1, 2};
int myrank, data = 2002, count, TAG = 0;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
if(myrank == 0)
{
MPI_Send(&data, 1, MPI_INT, 2, TAG, MPI_COMM_WORLD);
}
else if (myrank == 1) {
MPI_Send(&message, 3, MPI_INT, 2, TAG, MPI_COMM_WORLD);
}
else
{
source = status.MPI_SOURCE;
MPI_Get_count(...) ;
for (i = 0; i < count; i++){
buf[i] = (int *)malloc(count*sizeof(int));
}
MPI_Recv(&buf[0], count, MPI_INT, source, TAG, MPI_COMM_WORLD,
&status);
for (i = 0; i < count; i++){
printf("received: %d\n", buf[i]);
}
}
MPI_Finalize();
return 0;
}

Задание 4

В исходном тексте программы на языке Fortran пропущены вызовы процедур блокирующего зондирования и некоторые другие важные фрагменты. Добавить эти вызовы, откомпилировать и запустить программу.

program main_mpi
include 'mpif.h'
integer rank, i, k, ierr, tag, dest, ...
real x
tag = 0
dest = 2
call mpi_init(ierr)
call mpi_comm_rank(mpi_comm_world, rank, ierr)
if (rank.eq.0) then
i = 2002
call mpi_send(i, 1, mpi_integer, dest, tag, mpi_comm_world,
ierr)
else if(rank.eq.l) then
x = 3.14159
call mpi_send(x, 1, mpi_real, dest, tag, mpi_comm_world, ierr)
else
do k = 1, 2
if (status(mpi_source).eq.0) then
call mpi_recv(i, 1, mpi_integer, 0, tag, mpi_comm_world,
status, ierr)
print *, "received ", i, " from 0"
else
call mpi_recv(x, 1, mpi_real, 1, tag, mpi_comm_world, status,
ierr)
print *, "received ", x, " from 1"
end if
end do
end if
call mpi_finalize(ierr)
end

Задание 5

В исходном тексте программы на языке Fortran пропущены: вызов процедуры MPI_Wait и некоторые другие важные фрагменты. Добавить эти вызовы, откомпилировать и запустить программу.

program main_mpi
integer rank, tagl, tag2, cnt, ierr, ...
integer request
real sndbufl, sndbuf2, rcvbufl, rcvbuf2
cnt = 1
tag = 0
sndbufl = 3.14159
sndbuf2 = 2.71828
call mpi_comm_rank(mpi_comm_world, rank, ierr)
if (rank.eq.0) then
call mpi_ssend(sndbuf1, cnt, mpi_real, 1, tag1, mpi_comm_world,
ierr)
print *, "process ", rank, " send ", sndbuf1
call mpi_send(sndbuf2, cnt, mpi_real, 1, tag2, mpi_comm_world,
ierr)
print *, "process ", rank, " send ", sndbuf2
else
call mpi_irecv(rcvbuf1, cnt, mpi_real, 0, tag1, mpi_comm_world,
request, ierr)
call mpi_recv(rcvbuf2, cnt, mpi_real, 0, tag2, mpi_comm_world,
status, ierr)
print *, "process ", rank, " received before wait", rcvbuf1
print *, "process ", rank, " received before wait", rcvbuf2
print *, "process ", rank, " received after wait", rcvbuf1
print *, "process ", rank, " received after wait", rcvbuf2
end if
end
Евгений Звягин
Евгений Звягин
Россия, Липецк, Липецкий Государственный Технический Университет, 2014
Артур Гибадуллин
Артур Гибадуллин
Россия, Нижневартовск, ФГБОУ ВО НВГУ, Преподаватель