- •Лекция 1 Классификация многопроцессорных систем
- •Сетевой закон Амдала
- •Лекция 2 Техническая реализация многопроцессорных систем
- •Лекция 3 Система передачи сообщений mpi
- •Принципы работы mpich
- •Лекция 4 Структура mpi-программы
- •Int main(int argc, char* argv[]) { int procs_rank, procs_count; // Определяем переменные
- •Int mpi_Init (int* argc, char*** argv)
- •Int mpi_Comm_size(mpi_Comm comm, int *size)
- •Int mpi_Comm_rank(mpi_Comm comm, int *rank)
- •Int mpi_Finalize(void)
- •Определение времени выполнения mpi программы
- •Понятие коммуникатора
- •Лекция 5 Передача сообщений
- •Общие сведения о функциях передачи сообщений
- •Передача сообщения «точка-точка»
- •Int mpi_Send(void *buf, int count, mpi_Datatype type, int dest, int tag, mpi_Comm comm)
- •Прием сообщений «точка-точка» с блокировкой
- •Int mpi_Recv(void *buf, int count, mpi_Datatype type, int source, int tag, mpi_Comm comm, mpi_Status *status)
- •Int main(int argc, char* argv[])
- •Int ProcNum, ProcRank, RecvRank;
- •Лекция 6 Определение времени выполнения программы
- •Int s1; // Место под результат
- •Int size; // Размер принимаемого массива
- •Int Summa; // Сумма его элементов
- •Input n: 1000
- •Лекция 7 Функции неблокирующего обмена данными
- •Int mpi_Test(mpi_Request *request, int *flag,
- •Int mpi_Wait(mpi_Request *request, mpi_status *status)
- •Одновременное выполнение приема и передачи данных
- •Int mpi_Sendrecv (void *sbuf, int scount,
- •Int stag, void *rbuf, int rcount,
- •Int rtag, mpi_Comm comm,
- •Int mpi_Sendrecv_replace (void *buf, int count,
- •Int stag, int source,
- •Int rtag, mpi_Comm comm,
- •Лекция 8 Коллективные операции передачи данных
- •Функция синхронизации процессов
- •Int mpi_Barrier(mpi_Comm comm);
- •Широковещательная рассылка данных
- •Int mpi_Bcast (void* buffer, int count,
- •Int main(int argc, char* argv[])
- •Int proc_rank, proc_count;
- •Широковещательная рассылка индивидуальных данных
- •Int mpi_Scatter
- •Void *rbuf, int rcount, mpi_Datatype rtype,
- •Int root, mpi_Comm comm)
- •Лекция 9 Функции сбора блоков данных от всех процессов группы
- •Int mpi_Gather
- •Void* recvbuf, int recvcount, mpi_Datatype recvtype,
- •Int root, mpi_Comm comm);
- •Int array[100];
- •Int root, *rbuf, proc_count;
- •Int mpi_Allgather
- •Void *rbuf, int rcount, mpi_Datatype rtype,
- •Передача данных от всех процессов всем процессам
- •Int mpi_Alltoall
- •Void *rbuf, int rcount, mpi_Datatype rtype,
- •Лекция 10 Редукция данных
- •Int mpi_Reduce (void *sendbuf, void *recvbuf,
- •Int count, mpi_Datatype type,
Функция синхронизации процессов
Функция синхронизации процессов MPI_Barrier блокирует работу вызвавшего ее процесса до тех пор, пока все другие процессы группы также не вызовут эту функцию. Завершение работы этой функции возможно только всеми процессами одновременно (все процессы «преодолевают барьер» одновременно).
Int mpi_Barrier(mpi_Comm comm);
Синхронизация с помощью барьеров используется, например, для завершения всеми процессами некоторого этапа решения задачи, результаты которого будут использоваться на следующем этапе. Использование барьера гарантирует, что ни один из процессов не приступит раньше времени к выполнению следующего этапа, пока результат работы предыдущего не будет окончательно сформирован. Неявную синхронизацию процессов выполняет любая коллективная функция.
Широковещательная рассылка данных
Широковещательная рассылка данных выполняется с помощью функции MPI_Bcast. Процесс с номером root рассылает сообщение из своего буфера передачи всем процессам области связи коммуникатора comm.
Int mpi_Bcast (void* buffer, int count,
MPI_Datatype datatype, int root,
MPI_Comm comm);
В результате вызова функции каждый процесс в области связи коммуникатора comm, включая процесс-отправитель, получит копию сообщения от процесса-отправителя root. Отправляемые данные берутся, начиная с адреса buffer, в количестве count элементов типа MPI_Datatype.
В коде MPI-программы функция MPI_Bcast не должна вызываться только одним каким-то процессом.
Рассмотрим пример использования функции MPI_Bcast:
#include "stdafx.h"
#include <mpi.h>
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int proc_rank, proc_count;
int mas[10]; // Массив данных на 10 интов
int i;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &proc_count);
MPI_Comm_rank(MPI_COMM_WORLD, &proc_rank);
//==========================================================
if (proc_rank==0) // Если это "главный" процесс
{
// Заполняем массив числами от 1 до 10.
// Заполнение массива делается только в
// нулевом процессе. В остальных процессах
// массив изначально неинициализирован.
for (i=0; i<10; i++)
mas[i]=i+1;
// Участвуем в широковещательной рассылке массива:
MPI_Bcast (mas, 10, MPI_INT, 0, MPI_COMM_WORLD);
// Распечатываем полученный от самого себя массив:
for (i=0; i<10; i++)
cout << proc_rank << '_' << mas[i] << " ";
cout << '\n';
} // if - процесс 0
//==========================================================
if (proc_rank>0) // Если это не главный процесс
{
// Участвуем в широковещательной рассылке массива:
MPI_Bcast (mas, 10, MPI_INT, 0, MPI_COMM_WORLD);
// Распечатываем полученный от процесса 0 массив:
for (i=0; i<10; i++)
cout << proc_rank << '_' << mas[i] << " ";
cout << '\n';
}
//==========================================================
MPI_Finalize();
// закрываем MPI-библиотеку
return 0;
}
В данной программе с помощью блоков if выделены области кода, выполняемые определенными процессами. В частности, первый оператор if содержит блок кода для процесса с рангом 0, второй оператор if содержит блок кода для всех процессов с ненулевым рангом.
Процесс с рангом 0, запустившись, инициализирует массив mas числами от 1 до 10. При этом остальные процессы начальной инициализации не делают.
Рано или поздно все процессы дойдут до вызова функции MPI_Bcast. В процессе ее выполнения содержимое массива mas нулевого процесса будет скопировано в массив mas всех остальных процессов.
В заключение каждый из процессов распечатает на экране содержимое своего массива. Для наглядности перед значением каждого элемента через символ «_» печатается ранг соответствующего процесса.
Например, для восьми процессов результат работы программы может быть следующим:
5_1 5_2 5_3 5_4 5_5 5_6 5_7 5_8 5_9 5_10
2_1 2_2 2_3 2_4 2_5 2_6 2_7 2_8 2_9 2_10
6_1 6_2 6_3 6_4 6_5 6_6 6_7 6_8 6_9 6_10
0_1 0_2 0_3 0_4 0_5 0_6 0_7 0_8 0_9 0_10
4_1 4_2 4_3 4_4 4_5 4_6 4_7 4_8 4_9 4_10
7_1 7_2 7_3 7_4 7_5 7_6 7_7 7_8 7_9 7_10
3_1 3_2 3_3 3_4 3_5 3_6 3_7 3_8 3_9 3_10
1_1 1_2 1_3 1_4 1_5 1_6 1_7 1_8 1_9 1_10
Рассмотренный пример программы можно записать более кратко, если учесть, что:
– вызов функции MPI_Bcast синтаксически не отличается для процессов разных рангов;
– все процессы после вызова MPI_Bcast делают в точности одно и то же: распечатывают массив mas.
Тогда программа принимает следующий вид:
#include "stdafx.h"
#include <mpi.h>
#include <iostream>
using namespace std;
