Лабораторная работа №2 mpi: Передача данных с помощью блокирующих коммуникационных функций типа "Точка-Точка" Лабораторный практикум




Дата канвертавання18.04.2016
Памер118.29 Kb.


ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ

КЕМЕРОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

Кафедра ЮНЕСКО по новым информационным технологиям

Параллельное программирование


ЛАБОРАТОРНАЯ РАБОТА №2
MPI: Передача данных с помощью блокирующих коммуникационных функций типа “Точка-Точка”

Лабораторный практикум



Математический факультет

Специальность 010503 – математическое обеспечение и администрирование

информационных систем

Кемерово, 2013


  1. Цель работы:


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


Требования к результатам выполнения лабораторного практикума:

  • при выполнении задания необходимо сопровождать все проделанные действия скриншотами и описаниями к ним;

  • также необходимо придерживаться строгой последовательности действий при выполнении заданий;

  • сделать общий вывод;

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

При составлении и оформлении отчета следует придерживаться рекомендаций, представленных на странице http://unesco.kemsu.ru/student/rule/rule.html.
  1. Теоретический материал


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

Функция передачи сообщения MPI_Send

Синтаксис

FORTRAN:

INTEGER COUNT, DATATYPE, DEST, TAG, COMM, IERR

BUF(*)

MPI_SEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERR)

C:


int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest,

int tag, MPI_Comm comm)



Входные параметры:

buf

-

адрес начала расположения пересылаемых данных;

count

-

число пересылаемых элементов;

datatype

-

Тип посылаемых элементов;

dest

-

номер процесса-получателя в группе, связанной с коммуникатором comm;

tag

-

идентификатор сообщения;

comm

-

коммуникатор области связи.

Функция выполняет посылку count элементов типа datatype сообщения с идентификатором tag процессу dest в области связи коммуникатора comm. Переменная buf - это, как правило, массив или скалярная переменная. В последнем случае значение count = 1.

Функция приема сообщения MPI_Recv

Синтаксис

FORTRAN:



BUF(*)

INTEGER COUNT, DATATYPE, SOURCE, TAG, COMM

INTEGER STATUS(MPI_STATUS_SIZE), IERR

MPI_RECV(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, STATUS, IERR)

C:


int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source,

int tag, MPI_Comm comm, MPI_Status *status)



Входные параметры:

count

-

максимальное число принимаемых элементов;

datatype

-

тип элементов принимаемого сообщения;

source

-

номер процесса-отправителя;

tag

-

идентификатор сообщения;

comm

-

коммуникатор области связи.

Выходные параметры:

buf

-

адрес начала расположения принимаемого сообщения;

status

-

атрибуты принятого сообщения.

Функция выполняет прием count элементов типа datatype сообщения с идентификатором tag от процесса source в области связи коммуникатора comm.

Вместо параметра source можно использовать предопределенный параметр MPI_ANY_SOURCE - читать сообщение от любого отправителя, вместо параметра идентификатора сообщения tag - MPI_ANY_TAG - читать сообщение с любым идентификатором.

Атрибуты полученного сообщения доступны в переменных status. Переменные status должны быть явно объявлены в MPI программе. В языке C status - это структура типа MPI_Status с тремя полями MPI_SOURCE, MPI_TAG, MPI_ERROR. В языке FORTRAN status - массив типа INTEGER размера MPI_STATUS_SIZE. Константы MPI_SOURCE, MPI_TAG и MPI_ERROR определяют индексы элементов. Назначение полей переменной status представлено в таблице 1.

Таблица 1

Назначение полей переменной status


Поля status

C

FORTRAN

Процесс-отправитель

status.MPI_SOURCE

status(MPI_SOURCE)

Идентификатора сообщения

status.MPI_TAG

status(MPI_TAG)

Код ошибки

status.MPI_ERROR

status(MPI_ERROR)



Проблемы использования блокирующих коммуникационных функций типа “точка-точка”

Проблема взаимных блокировок

Например, 0-й процессор организует с помощью блокирующей функции MPI_Send посылку переменной 1-му процессору, в тоже время 1-й процессор организует пересылку переменной 0-му процессору, затем каждый из них запускает MPI_Recv для приема ожидаемой пересылки. Так как функция MPI_Send блокирует выполнение ветви программы до завершения приема принимающим процессором, то в данном случае оба процессора будут ждать окончания запущенных функций MPI_Send, которые не могут быть выполнены. Возникла тупиковая ситуация. Решить вопрос организации такой передачи данных можно несколькими способами: на одном из процессоров выставить функцию MPI_Recv прежде MPI_Send (последовательная передача данных), использовать специальную функцию одновременной передачи данных – MPI_Sendrecv (ее изучение будет на следующем занятии), которая не приводит к блокировке процессов.

Следствие: нельзя организовать передачу данных с помощью MPI_Send и MPI_Recv отдельно взятого процессора самому себе.

Неэффективность применения для многоточечной рассылки данных

В случае передачи одних и тех же данных от одного процессора всем остальным в группе использование функций MPI_Send и MPI_Recv малоэффективно, наиболее привлекательным является применение коллективных операций, например, широковещательная рассылка данных – MPI_Bcast, использование которых приводит к уменьшению программного кода и увеличению эффективности распараллеливания за счет более быстрого обмена данными.



  1. Примеры


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

  1. PROGRAM EXAMPLE

  2. INCLUDE 'mpif.h'

  3. INTEGER my_id, np, ierr, status(MPI_Status_size)

  4. REAL*8 Buf

  5. CALL MPI_Init(ierr)

  6. CALL MPI_Comm_Size(mpi_comm_world, np, ierr)

  7. CALL MPI_Comm_Rank(mpi_comm_world, my_id, ierr)

  8. IF (my_id.EQ.0) Then

  9. Buf=1

  10. CALL MPI_Send(Buf,1,MPI_REAL8,1,1, mpi_comm_world,ierr)

  11. else

  12. CALL MPI_Recv(Buf,1, MPI_REAL8,0,1, mpi_comm_world,status,ierr)

  13. Write(*,*) my_id,': Buf=',Buf

  14. Write(*,*) status(mpi_source),status(mpi_tag),status(mpi_error)

  15. end if

  16. CALL MPI_Finalize(ierr)

  17. STOP

  18. END

Пример 2. Передача переменной BUF от нулевого процессора всем процессорам.



  1. PROGRAM EXAMPLE

  2. INCLUDE 'mpif.h'

  3. INTEGER my_id, np, ierr, status(MPI_Status_size)

  4. REAL*8 Buf

  5. CALL MPI_Init(ierr)

  6. CALL MPI_Comm_Size(mpi_comm_world, np, ierr)

  7. CALL MPI_Comm_Rank(mpi_comm_world, my_id, ierr)

  8. IF (my_id.EQ.0) Then

  9. Buf=1

  10. DO I=1,NP-1

  11. CALL MPI_Send(Buf,1,MPI_REAL8,I,1, mpi_comm_world,ierr)

  12. END DO

  13. else

  14. CALL MPI_Recv(Buf,1, MPI_REAL8,0,1, mpi_comm_world,status,ierr)

  15. end if

  16. Write(*,*) my_id,': Buf=',Buf

  17. CALL MPI_Finalize(ierr)

  18. STOP

  19. END

Пример 3. Пересылка данных со всех процессоров на нулевой процессор.



  1. PROGRAM EXAMPLE

  2. INCLUDE 'mpif.h'

  3. INTEGER my_id, np, comm,ierr, status(mpi_status_size)

  4. INTEGER Buf(8)

  5. CALL MPI_Init(ierr)

  6. CALL MPI_Comm_Size(mpi_comm_world, np, ierr)

  7. CALL MPI_Comm_Rank(mpi_comm_world, my_id, ierr)

  8. IF (my_id.EQ.0) Then

  9. DO I=1,NP-1

  10. CALL MPI_Recv(Buf(I),1, MPI_INTEGER,i,1,mpi_comm_world,status,ierr)

  11. Write(*,*) my_id,': Buf=',Buf(i)

  12. END DO

  13. else

  14. CALL MPI_Send(MY_ID,1,MPI_INTEGER,0,1, mpi_comm_world,ierr)

  15. End IF

  16. CALL MPI_Finalize(ierr)

  17. STOP

  18. END

Пример 4. Пересылка данных между процессорами с использованием топологии ”кольцо”.

Пояснительный рисунок к примеру:

В примере переменные next и prev служат для задания на каждом процессоре номеров процессоров отправителей и получателей. В MPI существуют специальные функции для задания топологий. Далее при изучении функций задания топологий мы реализуем топологию кольцо, но уже с использованием функций MPI. Следует помнить, что нулевой процессор должен сначала отправить сообщение, а затем принимать, все остальные наоборот, сначала принимать сообщение, а потом отправлять.




  1. PROGRAM EXAMPLE

  2. INCLUDE 'mpif.h'

  3. INTEGER my_id, np, comm,ierr, status(mpi_status_size)

  4. INTEGER next, prev

  5. REAL*8 Buf

  6. CALL MPI_Init(ierr)

  7. CALL MPI_Comm_Size(mpi_comm_size, np, ierr)

  8. CALL MPI_Comm_Rank(mpi_comm_size, my_id, ierr)

  9. Write(*,*) 'Hello, ',my_id,' processor of ',np, 'processors'

  10. next=my_id+1

  11. prev=my_id-1

  12. IF (my_id.EQ.0) Then

  13. prev=np-1

  14. End IF

  15. IF (my_id.EQ.np-1) Then

  16. next=0

  17. End IF

  18. IF (my_id.EQ.0) Then

  19. Buf=1

  20. CALL MPI_Send(Buf,1,MPI_REAL8,next,1, mpi_comm_size,ierr)

  21. CALL MPI_Recv(Buf,1, MPI_REAL8,prev,1,mpi_comm_size,status,ierr)

  22. Else

  23. CALL MPI_Recv(Buf,1, MPI_REAL8,prev,1, mpi_comm_size,status,ierr)

  24. Buf=Buf+1

  25. CALL MPI_Send(Buf,1,MPI_REAL8,next,1, mpi_comm_size,ierr)

  26. End IF

  27. Write(*,*) my_id,': Buf=',Buf

  28. CALL MPI_Finalize(ierr)

  29. STOP

  30. END



  1. Вопросы


  1. Возможна ли пересылка при помощи блокирующих функций типа “точка-точка” сообщения процессора самому себе?

  2. Что такое атрибуты сообщения?

  3. Возвращает ли функция MPI_Send код ошибки, номер процесса, которому адресована передача?

  4. Происходит ли возврат из функции MPI_Send, когда можно повторно использовать параметры данной функции или когда сообщение покинет процесс, или когда сообщение принято адресатом или когда адресат инициировал приём данного сообщения?

  5. Может ли значение переменной count быть равным нулю?

  6. Можно ли в качестве значения тэга в команде посылки передать значение номера процесса в коммуникаторе?

  7. Может ли длина буфера получателя быть большей, чем длина принимаемого сообщения? А меньшей?

  8. Какую информацию содержит переменная status?

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

  10. Возможен ли прием меньшего количества элементов массива, чем было указано при отправке сообщения?

  11. В каком случае возникает тупиковая ситуация при использовании блокирующих функций типа “точка-точка”?

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

    1. MPI_COMM_SIZE

    2. MPI_COMM_RANK

    3. MPI_Send

    4. MPI_Recv



  1. Упражнения


    1. Напишите программу, в которой определено только два процесса. В одном из них осуществляется генерация случайных чисел, количество которых задает пользователь. Второй процесс получает эти числа, складывает их, посылает результат в первый процесс. Результат выводится на экран.

    2. Написать программу, используя блокирующие коммуникационные функции (MPI_Send, MPI_Recv), реализующую следующий алгоритм:

    1. на 0 процессоре инициализируется переменная (Real a);

    2. 0 процессор пересылает переменную а первому процессору;

    3. Первый, получив значение переменной а, добавляет к нему единицу и отправляет на нулевой процессор.

Вывод результатов должен быть оформлен следующим образом: номер процессора, посылаемое значение, полученное значение.

    1. Написать программу, используя блокирующие коммуникационные функции (MPI_Send, MPI_Recv), реализующую следующий алгоритм:

    1. на 0 процессоре инициализируется переменная (Real a);

    2. 0 процессор рассылает переменную а всем процессорам;

    3. после получения переменной а, все процессора прибавляют к ней свой индивидуальный номер и передают на 0 процессор;

    4. 0 процессор получает от всех процессоров данные и формирует массив, который выводится в файл результатов (формат файла результатов – номер процессора, пересланное им значение переменной а).

    1. Написать программу, используя блокирующие коммуникационные функции (MPI_Send, MPI_Recv), реализующую алгоритм передачи данных по кольцу одномерного массива.

    2. Написать программу, используя блокирующие коммуникационные функции (MPI_Send, MPI_Recv), реализующую алгоритм передачи данных по двум кольцам: нечетные процессора образуют 1 кольцо, четные – второе.

    3. Написать программу, используя блокирующие коммуникационные функции (MPI_Send, MPI_Recv), реализующую построчное (столбцовое) распределение двумерного массива, расположенного в памяти 0-го процессора.

    4. Написать программу для определения примерного времени t, затрачиваемого на выполнение одной арифметической операции. Для проведения численных экспериментов используйте тип данных удвоенной точности (REAL*8 или DOUBLE PRESIZION), для определения времени – функцию MPI_Wtime(), исследуемые операции – сложение, деление. Так как современные микропроцессоры способны выполнить несколько миллионов арифметических операций в секунду, то для наиболее достоверного определения результатов необходимо выполнить операции в цикле. Полученный результат сохраните для упражнений, выдаваемых по теоретической части курса. Попробуйте “развернуть” цикл – выполнить несколько операций за один проход цикла. Как изменился результат и почему?

    5. Написать программу для определения примерного времени на передачу одного машинного слова (значения переменной удвоенной точности – REAL*8 или DOUBLE PREZISION) при однонаправленных пересылках. Программу построить следующим образом:

    1. на 0 процессоре инициализируется переменная (REAL*8 a);

    2. 0 процессор засекает время t1=MPI_Wtime() и посылает переменную а первому процессору;

    3. первый процессор принимает значение переменной а и отправляет его на нулевой процессор;

    4. нулевой процессор получает значение а и засекает время t2=MPI_Wtime();

    5. t=(t2-t1)/2 будет искомое время.

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

    1. Написать программу для определения скорости передачи данных (для пересылки можно взять двумерный массив удвоенной точности – REAL*8 или DOUBLE PREZISION) при однонаправленных пересылках. Программу построить следующим образом:

    1. на 0 процессоре инициализируется массив a(n,n);

    2. 0 процессор засекает время t1=MPI_Wtime() и посылает данные первому процессору;

    3. первый процессор принимает данные и отправляет их на нулевой процессор;

    4. нулевой процессор получает данные и засекает время t2=MPI_Wtime();

    5. V=2(пересылки)*4(байта)*N*N(размерность массива)/(t2-t1) будет искомая скорость передачи данных поверх MPI при однонаправленной передаче данных.

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

  1. Упражнения для самостоятельного выполнения





  1. Напишите программу, которая проверяет, в каком порядке осуществляется передача сообщений. Для этого можно допустить, что все процессы за исключением процесса 0 посылают 100 сообщений процессу 0. Пусть процесс 0 распечатывает сообщения в порядке их приема, используя MPI_ANY_SOURCE и MPI_ANY_TAG в MPI_Recv.

  2. Написать программу, используя блокирующие коммуникационные функции (MPI_Send, MPI_Recv), реализующую алгоритм сдваивания при вычислении суммы ряда чисел. Размерность массива вводится при выполнении программы, элементы массива формируются на 0-ом процессоре, затем распределяются по процессорам, каждый процессор получает частичную сумму из полученных элементов и пересылает значение на нулевой процессор, нулевой процессор производит окончательное суммирование и выводит результат. При получении частичных сумм на нулевом процессоре используйте предопределенные константы – MPI_ANY_SOURCE, MPI_ANY_TAG. Обоснуйте их использование в данном частном случае. Определите время выполнения последовательной программы и параллельной в зависимости от размерности массива. Объясните особенности полученного результата.

  3. Написать программу, используя блокирующие коммуникационные функции (MPI_Send, MPI_Recv), реализующую алгоритм умножения матрицы на вектор. Размерность массива вводится при выполнении программы, элементы массива формируются на 0-ом процессоре, затем распределяются по процессорам, каждый процессор получает промежуточный результат и пересылает его на нулевой процессор, нулевой процессор производит окончательное формирование результата и выводит его. При выполнении данного упражнения можно использовать строчное или столбцовое распределение двумерного массива по процессорам. Определите время выполнения последовательной программы и параллельной в зависимости от размерности массива. Объясните особенности полученного результата.




База данных защищена авторским правом ©shkola.of.by 2016
звярнуцца да адміністрацыі

    Галоўная старонка