Из определения операции матричного умножения следует, что вычисление всех
элементов матрицы С может быть выполнено независимо
друг от друга. Как результат, возможный подход для организации параллельных
вычислений состоит в использовании в качестве базовой подзадачи процедуры
определения одного элемента результирующей матрицы С. Для проведения всех необходимых вычислений каждая
подзадача должна содержать по одной строке матрицы А
и одному столбцу матрицы В. Общее количество
получаемых при таком подходе подзадач оказывается равным n2 (по числу элементов матрицы С).
Алгоритм представляет собой итерационную процедуру, количество итераций
которой совпадает с числом подзадач. На каждой итерации алгоритма каждая
подзадача содержит по одной строке матрицы А и одному столбцу матрицы В. При
выполнении итерации проводится скалярное умножение содержащихся в подзадачах
строк и столбцов, что приводит к получению соответствующих элементов
результирующей матрицы С. По завершении вычислений в конце каждой итерации
столбцы матрицы В должны быть переданы между подзадачами с тем, чтобы в каждой
подзадаче оказались новые столбцы матрицы В и могли быть вычислены новые
элементы матрицы C. При этом данная передача столбцов между подзадачами должна
быть организована таким образом, чтобы после завершения итераций алгоритма в
каждой подзадаче последовательно оказались все столбцы матрицы В.
Выделенные
базовые подзадачи характеризуются одинаковой вычислительной трудоемкостью и
равным объемом передаваемых данных. Когда размер матриц n оказывается больше,
чем число процессоров p, базовые подзадачи можно укрупнить, объединив в рамках
одной подзадачи несколько соседних строк и столбцов перемножаемых матриц. В этом
случае исходная матрица A разбивается на ряд горизонтальных полос, а матрица B
представляется в виде набора вертикальных полос. Размер полос при этом
следует выбрать равным k=n/p (в предположении, что n кратно p), что позволит
по-прежнему обеспечить равномерность распределения вычислительной нагрузки по
процессорам, составляющим многопроцессорную вычислительную систему.
#include<stdafx.h>
#include<mpi.h>
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<math.h>
#include<time.h>
int
ProcNum;
int ProcRank;
int flag=0;
int Size;
double
*A; double *B; double *C;
//***********************************************************
void
PrintMatrix(double* pMatrix,int Size) {
for (int i=0;i<Size;i++)
{printf("\n");
for (int j=0;j<Size;j++)
printf("%7.4f ",pMatrix[i*Size+j]);
}
}
//------------------------------------------------------------
void
RandInit (double* pMatrix, int Size) {
srand(100);
for (int i=0;
i<Size; i++) {
for (int j=0;j<Size;j++)
pMatrix[i*Size+j]=rand()/double(1000);
}
}
//-------------------------------------------------
void
InitProcess (double* &A,double* &B,double* &C ,int &Size)
{
MPI_Comm_size(MPI_COMM_WORLD,
&ProcNum);
MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank);
if (ProcRank
== 0) {
do {printf("\n--Square matrix
multiplication--");
printf("\nPlease, enter
matrix size: "); scanf("%d", &Size);
if (Size< ProcNum) printf("Matrix size is
less than the number of processes! \n");
if
(Size%ProcNum!=0) printf("Matrix size should be dividable by the number of
processes! \n");
}
while ((Size<
ProcNum)||(Size%ProcNum!=0));
}
if (Size<10)
flag=1;
MPI_Bcast(&Size, 1, MPI_INT, 0, MPI_COMM_WORLD);
if (ProcRank == 0) {
A = new double
[Size*Size];
B = new double
[Size*Size];
C = new double
[Size*Size];
RandInit (A, Size); RandInit (B,
Size);
}
}
//-------------------------------------------------
void
Flip (double *&B, int dim) {
double temp=0.0;
for
(int i=0; i<dim; i++){
for (int j=i+1; j<dim;
j++){
temp=B[i*dim+j];B[i*dim+j]=B[j*dim+i];B[j*dim+i]=temp;
}
}
}
//-------------------------------------------------
void
MatrixMultiplicationMPI(double *&A, double *&B, double *&C, int
&Size) {
int dim =Size;
int i, j, k, p,
ind;
double temp;
MPI_Status Status;
int
ProcPartSize=dim/ProcNum;
int ProcPartElem=ProcPartSize*dim;
double* bufA=new double[dim*ProcPartSize];
double* bufB=new
double[dim*ProcPartSize];
double* bufC=new
double[dim*ProcPartSize];
int ProcPart = dim/ProcNum, part =
ProcPart*dim;
if (ProcRank == 0) {
Flip(B,Size);
}
MPI_Scatter(A, part, MPI_DOUBLE,
bufA, part, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Scatter(B, part,
MPI_DOUBLE, bufB, part, MPI_DOUBLE, 0, MPI_COMM_WORLD);
temp=0.0;
for(i=0;i<ProcPartSize;i++){
for(j=0;j<ProcPartSize;
j++){
for(k=0;k<dim;k++) temp+=bufA[i*dim+k]*bufB[j*dim+k];
bufC[i*dim+j+ProcPartSize*ProcRank]=temp;temp=0.0;
}
}
int NextProc; int PrevProc;
for(p=1; p<ProcNum; p++)
{
NextProc = ProcRank+1;
if (
ProcRank ==ProcNum-1 ) NextProc = 0;
PrevProc = ProcRank -
1;
if ( ProcRank == 0 ) PrevProc =
ProcNum-1;
MPI_Sendrecv_replace(bufB,part, MPI_DOUBLE,
NextProc, 0, PrevProc, 0, MPI_COMM_WORLD, &Status);
temp=0.0;
for (i=0; i<ProcPartSize;
i++) {
for(j=0; j<ProcPartSize; j++)
{
for(k=0; k<dim;
k++){
temp
+=bufA[i*dim+k]*bufB[j*dim+k];}
if (ProcRank - p
>= 0 )
ind=ProcRank -
p;
else ind=(ProcNum -
p+ProcRank);
bufC[i*dim+j+ind*ProcPartSize]=temp;
temp=0.0;
}
}
}
MPI_Gather(bufC,
ProcPartElem, MPI_DOUBLE, C, ProcPartElem, MPI_DOUBLE, 0, MPI_COMM_WORLD);
delete []bufA;
delete []bufB;
delete []bufC;
}
//--------------------------------------------------------
void main(int argc, char* argv[]) {
double beg, end,
serial, parallel=0;
MPI_Init ( &argc, &argv
);
InitProcess
(A,B,C,Size);
beg=MPI_Wtime();
MatrixMultiplicationMPI(A,B,C,Size);
end=MPI_Wtime(); parallel=
end-beg;
if (ProcRank == 0) {
printf("\n",&ProcNum);
printf
("\n");
printf("\nTime of execution - Parallel
calculation:\n");
printf("%7.4f",parallel);
if (flag)
{
printf("\nMatrix C - Parallel
calculation\n");
PrintMatrix(C,Size);
}
}
MPI_Finalize();
delete [] A; delete [] B; delete [] C;
getch();
}