Вы находитесь на странице: 1из 8

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ

РОССИИЙСКОЙ ФЕДЕРАЦИИ
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ
АВТОНОМНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ
ВЫСШЕГО ОБРАЗОВАНИЯ
«САМАРСКИЙ НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ
УНИВЕРСИТЕТ ИМЕНИ АКАДЕМИКА С.П. КОРОЛЕВА»

Институт информатики, математики и электроники


Факультет электроники и приборостроения
Кафедра суперкомпьютеров и общей информатики

ОТЧЕТ

по лабораторной работе № 3
«Перемножение матриц с использованием технологии MPI»

Выполнила: Логинова П. А.
гр. 6176-120404 D
Проверил: Минаев Е.Ю.

Самара 2019
Цель работы: осуществить перемножение матриц с использованием
технологии MPI. Исследовать, как влияют на работу программы изменения
различных параметров.
Ход работы:
#include "mpi.h"
#include <cstdlib>
#include <iostream>
#include "time.h"

using namespace std;

// MPI routines
int ProcNum, ProcRank;

void Flip (double *B, int Size)


{
double temp=0.0;
for (int i=0;i<Size;i++)
{
for (int j=i+1;j<Size;j++)
{
temp = B[i*Size+j];
B[i*Size+j] = B[j*Size+i];
B[j*Size+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[ProcPartElem];
double* bufB = new double[ProcPartElem];
double* bufC = new double[ProcPartElem];
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;

MPI_Finalize();
}

// Matrix output
template <typename T> int matrixOutput(T *Mat, int size, string name)
{
cout << "\"" << name << "\" matrix output:" << endl;
for (int i=0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
cout << Mat[i*size + j] << " ";
}
cout << endl;
}

return 1;
}

int main(int argc, char *argv[])


{
clock_t start;
if (argc != 2)
{
cout << "Program usage: " << endl <<"./" << argv[0] << " <n>" << endl << "where <n>
is the size of square matrix" << endl;
return -1;
}

const int N = atoi(argv[1]);

cout << "Begin initializing ..." << endl;

double *A, *B;

// Allocating memory for 2 initial matrices


A = new double[N*N];
B = new double[N*N];

// initializing matrix A and B with random numbers


for (int i=0; i < N; i++)
for (int j = 0; j < N; j++)
{
A[i*N + j] = (i+1) * (j+1);
B[i*N + j] = (i+1) + 2*(j+1);
}

//matrixOutput<double>(A, N, "A");
//matrixOutput<double>(B, N, "B");

double *C = new double[N*N];

cout << "Begin calculating ..." << endl;

start = clock();

// Begin MPI
MPI_Init(&argc, &argv);

MPI_Comm_size(MPI_COMM_WORLD, &ProcNum);
MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank);
//MPI_Bcast(&N, 1, MPI_INT, 0, MPI_COMM_WORLD);

MatrixMultiplicationMPI(A, B, C, N);
cout << endl << "Calculation time: " << double(clock() - start)/CLOCKS_PER_SEC << "
seconds" << endl;

/* // Sequantial matrix multiplication


for (int i=0; i < N; i++)
for (int j = 0; j < N; j++)
{
C[i*N + j] = 0;
for (int k = 0; k < N; k++)
{
C[i*N + j] += A[i*N + k] * B[k*N + j];
}
}
*/
//matrixOutput<double>(C, N, "resulting C");

// free memory
delete [] A;
delete [] B;
delete [] C;

return 0;
}

Кроме самой программы (формата .cpp), был еще следующий файл формата
.pbs:
#!/bin/bash
#PBS -N matMulMPI.mpi
#PBS -A stud000
#PBS -l nodes=4:ppn=2
#PBS -j oe
#PBS -l walltime=00:01:10

cd $PBS_O_WORKDIR

module load impi/4


mpirun -r ssh -machinefile $PBS_NODEFILE -np $PBS_NP ./matMulMPI.mpi 1000

В этом файле можно было менять 3 параметра:


 ppn;
 размерность матрицы;
 nodes.
Программой выводились разные времена, мы рассматривали
следующие значения времен: MPI time (время, затрачиваемое на
параллельную часть программы, сделанную с использованием MPI) и
Common time (общее время выполнения программы).
Было проведено следующее исследование:
1. Изменялся параметр ppn, а размерность матрицы оставалась
неизменной. Размерность матрицы была равна 1024. Измеренные времена
занесены в таблицу 1:
Таблица 1 – Зависимость времен от параметра ppn
Параметр ppn MPI time, с Common time, с
1 3,01 3,01
2 1,55 1,56
4 0,81 0,82
6 0,56 0,58

2. Изменялась размерность матрицы, а параметр ppn оставался


неизменным (ppn=2). Измеренные времена занесены в таблицу 2:
Таблица 2 – Зависимость времен от размерности матрицы
Размерность матрицы MPI time, с Common time, с
500 0,19 0,2
700 0,51 0,52
1024 1,55 1,56
1500 4,80 4,81

3. Изменялся параметр nodes, ppn=2 и размерность матрицы равна


1024. Измеренные времена занесены в таблицу 3:
Таблица 3 – Зависимость времен от параметра nodes
Параметр nodes MPI time, с Common time, с
1 3,04 3,04
2 1,55 1,56

Для полученных в таблицах 1 - 3 данных построим графики


зависимости времен от изменяемых параметров.
3.5

2.5

2
Время

1.5 Ряд1
Ряд2
1

0.5

0
0 2 4 6 8
Парметр ppn

Рисунок 1 – График зависимости времен от параметра ppn


6

4
Время

3
Ряд1
2 Ряд2

0
0 500 1000 1500 2000
Размерность матрицы

Рисунок 2 – График зависимости времен от размерности матрицы


3.5

2.5

Время 1.5 Ряд1


Ряд2
1

0.5

0
0 0.5 1 1.5 2 2.5
Парметр nodes

Рисунок 3 – график зависимости времен от параметра nodes


Вывод: по графикам, изображенным на рисунках 1-3, можно сделать
следующие выводы:
 с увеличением параметра ppn MPI time и Common time убывают;
 с увеличением размерности матрицы MPI time и Common time
увеличиваются;
 при изменеии параметра nodes MPI time и Common time убывают;
 при изменении ppn, nodes, размерности матрицы времена MPI time и
Common time сопоставимы друг с другом. Это означает, что
практически вся программа выполняется за то же время, за какое
выполняется и распараллеливание процессов, т.е. почти все время
уходит на то, чтобы программа была параллельной. Следовательно при
распараллеливании экономится время на общее выполнение
программы, поскольку не требуется затрачивать время на выполнение
последовательной части программы, как, к примеру, это было при
использовании технологии OpenMP.