МПСиВ. Лекция 4. Коллективные операции передачи данных

advertisement
Лекция 4
Параллельное программирование.
MPI. Коллективные операции
передачи данных
Методология
параллельных систем и
вычислений.
Доцент М.А. Сокольская
План.
1.
2.
3.
4.
5.
2
Обобщенная передача данных от всех процессов
одному процессу
Обобщенная передача данных от одного процесса
всем процессам
Общая передача данных от всех процессов всем
процессам.
Дополнительные операции редукции данных
Пример. Умножение матрицы на вектор
Коллективные операции передачи
данных…
Определение.
Под коллективными операциями в MPI
понимаются операции данных, в которых
принимают
участие
все
процессы
используемого коммуникатора.
Если какой-то процесс коммуникатора завершил
коллективную операцию, это не означает, что
операцию завершили остальные процессы.
3
Синхронизация процессов
MPI_Barrier(MPI_COMM comm, MPI_INT err);
Процедура
барьерной
синхронизации
процессов.
Работа
всех
процессов
коммуникатора comm блокируется до тех
пор, пока все процессы коммуникатора не
вызовут эту функцию
4
Распределение данных
Распределение данных – ведущий процесс (root) передает
процессам различающиеся данные
int MPI_Scatter(void *sbuf, int scount, MPI_Datatype stype, void *rbuf,
int rcount, MPI_Datatype rtype, int root, MPI_Comm comm),
где
- sbuf, scount, stype - параметры передаваемого сообщения
(scount определяет количество элементов, передаваемых на
каждый процесс),
- rbuf, rcount, rtype - параметры сообщения, принимаемого в
процессах,
- root – ранг процесса, выполняющего рассылку данных,
- comm - коммуникатор, в рамках которого выполняется передача
данных.
5
Распределение данных


Вызов MPI_Scatter при выполнении рассылки данных должен
быть обеспечен в каждом процессе коммуникатора,
MPI_Scatter
передает
всем
процессам
сообщения
одинакового размера. Если размеры сообщений для
процессов могут быть разными, следует использовать
функцию MPI_Scatterv.
0
0
0
1
1
1


root
0 1 2

p-1

root

а) до начала операции
root

p-1
p-1
6

p-1
б) после завершения операции
Пример использования
MPI_Scatter();
…
int rbuf [100], Size;
int root, *array;
MPI_Comm_size(MPI_COMM_WORLD, &Size);
array=new int [Size*100];
MPI_Scatter(array, 100, MPI_INT, rbuf, 100,
MPI_INT, root, MPI_COMM_WORLD);
7
Сбор данных
Сбор данных - передача данных от всех процессоров одному
процессу является обратной к операции распределения
данных
int MPI_Gather(void *sbuf, int scount, MPI_Datatype stype, void
*rbuf, int rcount, MPI_Datatype rtype, int root, MPI_Comm comm),
где
- sbuf, scount, stype - параметры передаваемого сообщения,
- rbuf, rcount, rtype - параметры принимаемого сообщения,
- root – ранг процесса, выполняющего сбор данных,
- comm - коммуникатор, в рамках которого выполняется передача
данных.
8
Сбор данных

MPI_Gather определяет коллективную операцию, и
ее вызов при выполнении сбора данных должен
быть обеспечен в каждом процессе коммуникатора
0
0
0
1
1
1


root
0 1 2

p-1

root

а) после завершения операции
root

p-1
p-1
9

p-1
б) до начала операции
Сбор и рассылка данных
10
MPI_Gather собирает данные на одном процессе.
Для получения всех собираемых данных на каждом
процессе нужно использовать функцию сбора и
рассылки:
int MPI_Allgather(void *sbuf, int scount, MPI_Datatype
stype, void *rbuf, int rcount, MPI_Datatype rtype,
MPI_Comm comm)
В случае, когда размеры передаваемых процессами
сообщений могут быть различны, для передачи
данных
необходимо
использовать
функции
MPI_Gatherv и MPI_Allgatherv.
Общая передача данных от всех
процессов всем процессам…
0
0
00 0 1


0 (p-1)

1 (p-1)

10 11


(p-1) 0
01 11

(p-1) 1
i  0 i 1

(p-1) i


i



(p-1)(p-1)
а) до начала операции
i

i (p-1)

(p-1)0 (p-1)1
11

1
1
p-1
00 10
0 i 1 i
p-1

0(p-1) 1(p-1)

(p-1) (p-1)
б) после завершения операции
Дополнительные операции
редукции данных…
12
MPI_Reduce обеспечивает получение
результатов редукции данных только на
одном процессе,
Функция MPI_Allreduce редукции и рассылки
выполняет рассылку между процессами всех
результатов операции редукции:
int MPI_Allreduce(void *sendbuf, void *recvbuf,int
count, MPI_Datatype type, MPI_Op op,
MPI_Comm comm)
Дополнительные операции
редукции данных…
Возможность управления распределением этих
данных между процессами предоставляется
функцией MPI_Reduce_scatter,
Функция MPI_Scan производит операцию сбора и
обработки данных, при которой обеспечивается
получение и всех частичных результатов
редуцирования
int MPI_Scan(void *sendbuf, void *recvbuf,int count,
MPI_Datatype type, MPI_Op op,MPI_Comm comm)
13
Дополнительные операции
редукции данных…
При выполнении функции MPI_Scan элементы получаемых
сообщений представляют собой результаты обработки
соответствующих элементов передаваемых процессами
сообщений, при этом для получения результатов на процессе с
рангом i, 0 i<n, используются данные от процессов, ранг
которых меньше или равен i
0
x00 x01 x02

x0,n-1
0
y00 y01 y02

y0,n-1
1
x10 x11 x12

x1,n-1
1
y10 y11 y12

y1,n-1
i
yi0 yi1 yi2

yi,n-1
p-1
yn-1,0 yn-1,1

i
xi0 xi1 xi2

14
xn-1,0 xn-1,1

xi,n-1

p-1




xn-1,n-1
а) до начала операции


yn-1,n-1
б) после завершения операции
Умножение матрицы на вектор
a0,1 , ..., a0,n 1   b0 
 c0   a0, 0 ,


 

...
  b1 
 c1   
 b 
c  a
,
a
,
...,
a
m 1,1
m 1, n 1   n 1 
 m 1   m 1, 0
Задача умножения матрицы на вектор может быть
сведена к выполнению m независимых операций
умножения строк матрицы A на вектор b
n
ci  ai , b   ai j bj , 0  i  m
15
j 1
Способы распределения данных:
ленточная схема
Непрерывное (последовательное) распределение
горизонтальные полосы
16
вертикальные полосы
A  ( A0 , A1 ,..., A p 1 )T ,
A  ( A0 , A1 ,..., A p 1 ),
Ai  (ai0 , ai1 ,..., aik 1 ),
Ai  ( i0 ,  i1 ,...,  ik 1 ) ,
i j  ik  j , 0  j  k , k  m / p
i j  il  j , 0  j  l , l  n / p
( ai , 0i  m,  ст роки м ат рицыA)
( i , 0  i  т,  ст олбцым ат рицыA)
Последовательный алгоритм
for ( i = 0; i < m; i++ )
{
c [ i ] = 0;
for ( j = 0; j < n; j++ )
{
c [ i ] += A [ i ][ j ] * b [ j ];
}
}
17
Умножение матрицы на вектор
Распределение данных – ленточная
схема (разбиение матрицы по
строкам)
Базовая подзадача - операция скалярного
умножения одной строки матрицы на
вектор
18
Выделение информационных
зависимостей
19
Базовая подзадача для выполнения вычисления
должна содержать строку матрицы А и копию
вектора b.
После завершения вычислений каждая базовая
подзадача будет содержать один из элементов
вектора результата c
Для объединения результатов расчетов и
получения полного вектора c на каждом из
процессоров
вычислительной
системы
необходимо выполнить операцию обобщенного
сбора данных
Программная реализация
Главная функция программы реализует логику работы алгоритма,
последовательно вызывая необходимые подпрограммы.
void main (int argc, char *argv[ ])
{
double *pMatrix; //исходная матрица
double *pVector; //исходный вектор
double *pResult; //результат умножения
int Size; //размеры исходных матрицы и вектора
double *pProcRows; //часть матрицы на процессе
double *pProcResult; //результаты вычислений процесса
int RowNum;
double Start, Finish, Duration;
int ProcNum, ProcRank;
20
Программная реализация
21
MPI_Init (&argc, &argv);
MPI_Comm_size (MPI_COMM_WORLD, &ProcNum);
MPI_Comm_rank (MPI_COMM_WORLD, &ProcRank);
//выделение памяти и инициализация исходных
//данных
ProcInit (pMatrix, pVector, pResult, pProcRows,
pProcResult, Size, RowNum);
//распределение исходных данных между
//процессорами
DataDistrib (pMatrix, pProcRows, pVector, Size,
RowNum);
Программная реализация
//параллельное выполнение умножения матрицы на
вектор
ParalResCalc (pProcRows, pVector, pProcResult, Size,
RowNum);
//сбор результирующего вектора на всех процессах
ResReplication (pProcResult, pResult, Size, RowNum);
//завершение процесса вычислений
ProcTermination (pMatrix, pVector, pResult, pProcRows,
pProcResult);
MPI_Finalize ();
22
}
Функция ProcInit
23
Задает размер и элементы для матрицы A и вектора b
void ProcInit (double *pMatrix, double *pVector, double
*pResult, double *pProcRows, double *pProcResult, int
Size, int RowNum)
{
int RestRows; //кол-во строк матрицы, которые еще не
//распределены
int i;
if (ProcRank == 0)
{
do
{
Функция ProcInit
printf(“\nВведите размер матрицы: ”);
scanf(“%d”, &Size);
if (Size < ProcNum)
printf (“Малый размер\n”);
} while (Size < ProcNum);
24
}
MPI_Bcast(&Size, 1, MPI_INT, 0,
MPI_COMM_WORLD);
RestRows = Size;
for (i = 0; i < ProcRank; i++)
RestRows = RestRows – RestRows/(ProcNum - 1)
RowNum = RestRows / (ProcNum - ProcRank);
Функция ProcInit
pVector = new double [Size];
pResult = new double [Size];
pProcRows = new double [RowNum * Size];
pProcResult = new double [RowNum];
if (ProcRank == 0)
{
pMatrix = new double [Size * Size];
RandomDataInit (pMatrix, pVector, Size);
//функцию задать самостоятельно
}
}
25
Функция DataDistrib
26
Осуществляет рассылку вектора b и распределение
строк исходной матрицы A по процессам.
void DataDistrib (double *pMatrix, double *pProcRows,
double *pVector, int Size, int RowNum)
{
int *pSendNum; //кол-во элементов, посылаемых
//процессу
int *pSendInd; //индекс первого элемента данных,
//посылаемого процессу
int RestRows = Size; //кол-во строк матрицы, которые
//еще не распределены
MPI_Bcast (pVector, Size, MPI_DOUBLE, 0,
MPI_COMM_WORLD);
Функция DataDistrib
27
//выделение памяти для хранения временных
//объектов
pSendInd = new int [ProcNum];
pSendNum = new int [ProcNum];
//определение положения строк матрицы,
//предназначенных каждому процессу
RowNum = Size / ProcNum;
pSendNum [ 0 ] = RowNum + Size;
pSendInd [ 0 ] = 0;
for (int i = 1; i < ProcNum; i++)
{
RestRows = RestRows – RowNum;
Функция DataDistrib
RowNum = RestRows / (ProcNum - i);
pSendNum [ i ] = RowNum * Size;
pSendInd [ i ] = pSendInd [ i-1 ] + pSendNum [ i-1 ];
};
//рассылка строк матрицы
MPI_Scatterv (pMatrix, PSendNum, pSendInd,
MPI_DOUBLE, pProcRows, pSendNum[ProcRank],
MPI_DOUBLE, 0, MPI_COMM_WORLD);
//освобождение памяти
delete [] pSendNum;
delete [] pSendInd;
28
}
Функция ParalResCalc
29
Вычисление части результирующего вектора
void ParalResCalc (double *pProcRows, double *pVector,
double *pProcResult, int Size, int RowNum) {
int i, j;
for (i = 0; i < RowNum; i++)
{
pProcResult [i] = 0;
for (j = 0; j < Size; j++)
pProcResult [i] = pProcResult [ i ] +
pProcRows[i * Size + j] * pVector[ j ];
}
}
Функция ResReplication
Объединяет блоки результирующего вектора c,
полученного на разных процессах и копирует вектор
результата на все процессы.
void ResReplication (double *pProcResult, double
*pResult, int Size, int RowNum) {
int *pReceiveNum; //кол-во элементов, посылаемых
процессом
int *pReceiveInd; //индекс элемента данных в
результирующем векторе
int RestRows = Size; //кол-во строк матрицы, которые
еще не распределены
int i;
30
Функция ResReplication
//выделение памяти для временных объектов
pReceiveInd = new int [ProcNum];
pReceiveNum = new int [ProcNum];
//определение положения блоков результирующего вектора
pReceiveInd [ 0 ] = 0;
pReceiveNum [ 0 ] = Size / ProcNum;
for (i = 1; i < ProcNum; i++)
{
RestRows = RestRows – pReceiveNum [i-1];
pReceiveNum [i] = RestRows / (ProcNum - 1);
pReceiveInd [i] = pReceiveInd [i-1] + pReceiveNum [i-1]
}
31
Функция ResReplication
//сбор всего вектора на всех процессах
MPI_Allgatherv (pProcResult,
pReceiveNum[ProcRank], MPI_DOUBLE,
pResult, pReceiveNum, pReceiveInd,
MPI_DOUBLE, MPI_COMM_WORLD);
//освобождение памяти
delete [] pReceiveNum;
delete [] pReceiveInd;
}
32
Самостоятельно
1.
Дописать функции для примера
a)
b)
2.
33
RandomDataInit (pMatrix, pVector, Size)
ProcTermination (pMatrix, pVector, pResult,
pProcRows, pProcResult).
Запустить доработанную программу на
кластере.
Итоги
Мы рассмотрели:
Функции коллективной передачи данных,
функции управления группами процессов и
коммуникаторами, пример параллельной
программы умножения матрицы на вектор.
34
Download