Сложные типы данных в MPI
Ранее во всех операциях передачи сообщений использовались непрерывные буферы, содержащие некоторое количество элементов одного типа. Но этого не всегда достаточно. Иногда существует необходимость передать сообщение, содержащее данные разных типов (например, состоящее из целочисленного размера сообщения, за которым следуют числа с плавающей точкой в соответствующем количестве) либо передать сообщение, в котором данные идут разрывно (например, минор матрицы). Одним из возможных решений является упаковка таких данных в непрерывный буфер на стороне отправителя и распаковка на стороне получателя. Недостаток такого решения в том, то необходимо дополнительное копирование в новый буфер, что влечет за собой ухудшение быстродействия и дополнительные расходы памяти. Вместо этого, в MPI есть средства для определения разрывных буферов, содержащих разнотипные данные. Тип данных в MPI определяет следующее
Последовательность исходных типов, которые тоже могут быть составными
Последовательность смещений в байтах
Смещения не обязательно должны быть положительными, различными, или идти по возрастанию. Таким образом, порядок отслылаемых элементов может не совпадать с порядком их хранения.
Создание новых типов данных
int MPI_Type_contiguous(int count, MPI_Datatype oldtype, MPI_Datatype *newtype)
По адресу newtype создает тип данных, каждый элемент которого представляет собой countэлементов типа oldtype, расположенных подряд
int MPI_Type_vector(int count, int blocklength, int stride, MPI_Datatype oldtype, MPI_Datatype *newtype)
count — число блоков объектов типа oldtype, из которых состоит один объект типа *newtype;
oldtype — исходный тип
blocklength — число элементов типа oldtype в одном блоке
stride — расстояние между блоками в размерах типа oldtype
newtype — адрес, по которому создается новый тип.
int MPI_Type_hvector(int count, int blocklength, MPI_Aint stride, MPI_Datatype oldtype, MPI_Datatype *newtype)
count — число блоков объектов типа oldtype, из которых состоит один объект типа *newtype;
oldtype — исходный тип
blocklength — число элементов типа oldtype в одном блоке
stride — расстояние между блоками в байтах
newtype — адрес, по которому создается новый тип.
Для конструирования типа, состоящего из блоков с разным числом элементов, отстоящих друг от друга на разное расстояние, используется следующая пара функций:
int MPI_Type_indexed(int count, int *array_of_blocklengths, int *array_of_displacements,
MPI_Datatype oldtype, MPI_Datatype *newtype)
count — число блоков объектов типа oldtype, из которых состоит один объект типа *newtype;
array_of_blocklengths — массив длин блоков;
array_of_displacements — массив расстояний между блоками в в размерах типа oldtype;
oldtype — исходный тип
newtype — адрес, по которому создается новый тип.
int MPI_Type_hindexed(int count, int *array_of_blocklengths, MPI_Aint *array_of_displacements,
MPI_Datatype oldtype, MPI_Datatype *newtype)
count — число блоков объектов типа oldtype, из которых состоит один объект типа *newtype;
array_of_blocklengths — массив длин блоков;
array_of_displacements — массив расстояний между блоками в байтах;
oldtype — исходный тип
newtype — адрес, по которому создается новый тип.
Для создания нового типа из нескольких различных существующих типов используется следующая функция
int MPI_Type_struct(int count, int *array_of_blocklengths, MPI_Aint *array_of_displacements,
MPI_Datatype* array_of_types, MPI_Datatype *newtype)
count — число элементов в массивах;
array_of_blocklengths — массив длин блоков;
array_of_displacements — массив расстояний между блоками в байтах;
array_of_types — массив исходных типов
newtype — адрес, по которому создается новый тип.
Прежде, чем использовать новый тип, необходимо вызвать следующую функцию:
int MPI_Type_commit(MPI_Datatype *datatype)
По завершению работы с типом необходимо вызвать
int MPI_Type_free(MPI_Datatype *datatype)
MPI_LB и MPI_UB
Иногда необходимо явно определять верхнюю и нижнюю границы области памяти, занимаемой типом (как в следующем примере). Для этого существуют встроенные типы MPI_LB и MPI_UB Нижняя граница области памяти определяется следующим образом:
if(ни один подтип не lb)
lb(typemap)=min (disp[j]);
else
lb(typemap)=min (disp[j], type[j]==lb)
Верхняя граница определяется аналогично:
if(ни один подтип не ub)
lb(typemap)= max(disp[j]);
else
lb(typemap)=max(disp[j], type[j]==ub)
Пример: Умножение матрицы на матрицу с использованием типов данных для строки и столбца
#include<mpi.h>
#include <stdio.h>
#include<stdlib.h>
int main(int argc, char** argv)
{
int rows;
int rank,size,sizeofrow,sizeoffloat, blen[2],disp[2];
float* matrix1,*matrix2,*result;
float* lm1,*lm2,*lr;
int* counts, *displs;
int offset;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
MPI_Datatype row,column[2],tosend;
if(argc!=2)
{
if(!rank)
fprintf(stderr,"usage: matrixfragment filename\n");
exit(1);
}
if(!rank)
{
freopen(argv[1],"r",stdin);
scanf("%d",&rows);
matrix1= (float*)malloc(sizeof(float)*rows*rows);
matrix2= (float*)malloc(sizeof(float)*rows*rows);
for(int i=0;i<rows*rows;i++)
scanf("%f",matrix1+i);
for(int i=0;i<rows*rows;i++)
scanf("%f ",matrix2+i);
result=(float*)malloc(sizeof(float)*rows*rows);
}
else
{
matrix1=NULL;
matrix2=NULL;
}
MPI_Bcast(&rows,1,MPI_INT,0,MPI_COMM_WORLD);
lm1=(float*)malloc(sizeof(float)*rows*rows);
lm2=(float*)malloc(sizeof(float)*rows*rows);
lr=(float*)malloc(sizeof(float)*rows*rows);
displs= (int*)malloc(sizeof(int)*size);
counts= (int*)malloc(sizeof(int)*size);
MPI_Type_contiguous(rows,MPI_FLOAT,&row);
MPI_Type_vector(rows,1,rows,MPI_FLOAT,column);
MPI_Type_commit(&row);
MPI_Type_commit(column);
MPI_Type_extent(MPI_FLOAT, &sizeoffloat);
blen[0] = 1;
blen[1] = 1;
disp[0] = 0;
disp[1] = sizeoffloat;
column[1] = MPI_UB;
MPI_Type_struct(2, blen, disp, column, &tosend);
MPI_Type_commit(&tosend);
for(int i=0;i<size;i++)
{
counts[i]=(rows/size +(i<(rows%size)));
displs[i]=0;
for(int j=0;j<i;j++)
displs[i]+=counts[j];
}
MPI_Scatterv(matrix1,counts,displs,row,
lm1,counts[rank],row,0,MPI_COMM_WORLD);
MPI_Scatterv(matrix2,counts,displs,tosend,
lm2,counts[rank],row,0,MPI_COMM_WORLD);
for(int i=0;i<counts[rank];i++)
for(int j=0;j<rows;j++)
lr[i*rows+j]=0;
for(int k=0;k<size;k++)
{
offset=(displs[(rank-k+size)%size]);
for (int i=0;i<counts[rank];i++)
for (int j=0;j<counts[(rank-k+size)%size];j++)
{
for(int l=0;l<rows;l++)
{
lr[i*rows+j+offset]+=lm1[i*rows+l]*lm2[j*rows+l];
}
}
MPI_Send(lm2,counts[(rank-k+size)%size],row,(rank+1)%size,k,MPI_COMM_WORLD);
MPI_Recv(lm2,counts[(rank+size-k-1)%size],row,(rank-1+size)%size,k,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
}
MPI_Gatherv(lr, counts[rank], row,
result, counts, displs, row, 0, MPI_COMM_WORLD);
if(!rank)
{
printf("====================\n");
for(int i=0;i<rows;i++)
{
for(int j=0;j<rows;j++)
printf("%g ",result[i*rows+j]);
printf("\n");
}
//*/
free( matrix1);
free (matrix2);
free (result);
}
free (lm1);
free (lm2);
free (lr);
MPI_Finalize();
return 0;
}
;
Задание 7
Написать программу умножения матриц в топологии «двумерная решетка» с использованием типов данных для столбца и минора матрицы.