Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

malyshkin_ve_korneev_vd_-_parallelnoe_programmirovanie_multikompyuterov

.pdf
Скачиваний:
68
Добавлен:
28.03.2016
Размер:
3.12 Mб
Скачать

* Решение СЛАУ методом простой итерации. Распределение данных - горизонтальными полосами. (Запуск примера на 8-ми компьютерах). */

#include<stdio.h>

#include<mpi.h>

#include<sys/time.h>

/* Каждая ветвь задает размеры своих полос матрицы MA и вектора правой части. (Предполагаем, что размеры данных делятся без остатка на количество компьютеров.) */

#define M 64 #define N 8

/*

Задаем необходимую точность приближения корней

*/

 

#define E 0.00001

/* Задаем шаг итерации */

#define T 0.01

/*Описываем массивы для полос исходной матрицы -MA, вектора правой части - F, значения приближений на предыдущей итерации - Y и текущей - Y1, результата умножения матрицы коэффициентов на вектор - S, и всего вектора значения приближений на предыдущей итерации - V. */

static double MA[N][M], F[N], Y[N], Y1[N], S[N] ; static double V[M], Fm, Fmp, Xm, Xmp;

int main(int argc, char **argv)

{int i, j, z, rank, size, v, it; int *index, *edges;

MPI_Comm comm_gr; struct timeval tv1, tv2; int dt1, reord = 1;

/* Инициализация библиотеки */

398

MPI_Init(&argc, &argv);

/* Каждая ветвь узнает размер системы */ MPI_Comm_size(MPI_COMM_WORLD, &size);

/* и свой номер (ранг) */ MPI_Comm_rank(MPI_COMM_WORLD, &rank);

/* Выделяем память под массивы для описания вершин и ребер в топологии полный граф */

index = (int *)malloc(size * sizeof(int)); edges=(int *)malloc(size*(size-1)*sizeof(int));

/* Заполняем массивы для описания вершин и ребер для топологии полный граф и задаем топологию "полный граф". */

for(i = 0; i < size; i++)

{index[i] = (size - 1)*(i + 1); v = 0;

for(j = 0; j < size; j++) { if(i != j)

edges[i * (size - 1) + v++] = j;

}

}

MPI_Graph_create(MPI_COMM_WORLD, size, index, edges, reord, &comm_gr);

/* Каждая ветвь генерирует свои полосы матрицы A

исвой отрезок вектора правой части.

*(По диагонали исходной матрицы - числа = 2, остальные числа = 1). */

for(i = 0; i < N; i++)

{for(j = 0; j < M; j++)

{ if((N*rank + i) == j) MA[i][j] = 2.0;

else

MA[i][j] = 1.0;

}

F[i] = M + 1;

}

/* Каждая ветвь задает начальное приближение корней. */

399

for(Fm = 0,i = 0; i < N; i++)

{Y1[i] = 0.8;

Fm += F[i] * F[i];

}

/* Находим ||f||. Суммируем Fm по всем компьютерам и запоминаем в каждом. Каждая параллельная ветвь будет иметь ||f||. */ MPI_Allreduce(&Fm,&Fmp,1,MPI_DOUBLE,MPI_SUM,

comm_gr);

gettimeofday(&tv1,NULL);

it = 0;

/* Начало вычислений. Главный цикл. */ do

{ for(i = 0; i < N; i++) Y[i] = Y1[i];

/* В каждой ветви формируем весь вектор предыдущей итерации и умножаем матрицу коэффициентов на этот вектор

*/

MPI_Allgather(Y, N, MPI_DOUBLE, V, N,

MPI_DOUBLE, comm_gr);

for(Xm = 0, i = 0; i < N; i++)

{ for(S[i] = 0, j = 0; j < M; j++) S[i] += MA[i][j] * V[j];

Y1[i] = Y[i] - T*(S[i] - F[i]);

Xm += (S[i] - F[i]) * (S[i] - F[i]);

}

it++;

MPI_Allreduce(&Xm, &Xmp, 1, MPI_DOUBLE, MPI_SUM, comm_gr);

}

while(Xmp/Fmp > E*E);

/* конец основного цикла */

/* Все ветви засекают время и его значение выводят на монитор */

gettimeofday(&tv2, NULL);

dt1 = (tv2.tv_sec - tv1.tv_sec) * 1000000 +

400

tv2.tv_usec - tv1.tv_usec; printf(" rank = %d Time = %d\n", rank, dt1);

/* Все ветви печатают, для контроля, свои первые четыре значения корня */

printf(" rank = %d Y0=%f Y1=%f Y2=%f Y3=%f\n", rank,Y[0],Y[1],Y[2],Y[3]);

/* Все ветви завершают вычисления */

MPI_Finalize(); return(0);

}

8.2.3.2 Параллельный алгоритм решения СЛАУ методом сопряженных градиентов. Дана система линейных алгебраических уравнений:

Ax = f

Этот метод предназначен для симметричных матриц:

aij = aji.

Выбирается начальное приближение вектора решений: x0.

xk

= xk-1+ αkzk-1

вычисление вектора решений на к-й

текущей итерации;

 

 

r0

= f – Ax0

начальное приближение вектора невязки;

rk

= rk-1- αkAzk-1

вычисление вектора невязки на к

текущей итерации;

 

 

αk

= (rk-1,rk-1)/(Azk-1,zk-1) коэффициент (здесь к

это номер итерационного шага);

z0

= r0 начальное приближение вектора спуска;

zk

= rk+ βkzk-1

вычисление вектора спуска на к-й текущей

итерации (сопряженное направление).

βk

= (rk,rk)/(rk-1,rk-1) коэффициент;

401

Условие выхода из итерационного процесса:

rk < ε f

Для решения этой задачи на параллельной системе исходную матрицу коэффициентов А разрезаем на p1 горизонтальных полосы по строкам, где p1 – количество компьютеров в системе. Аналогично, горизонтальными полосами разрезаются вектор f (правая часть) и вектора xk, rk, zk. Полосы последовательно распределяются по соответствующим компьютерам системы. Завершение вычислений аналогично, как и в предыдущем алгоритме.

/* * Решение СЛАУ методом сопряженных градиентов.

Распределение данных - горизонтальными полосами. (Запуск примера на 8-ми компьютерах). */ #include<stdio.h>

#include<mpi.h>

#include<time.h>

#include<sys/time.h>

/* Каждая ветвь задает размеры своих полос матрицы MA и вектора правой части. (Предполагаем, что размеры данных делятся без остатка на количество компьютеров.) */

#define M 64 #define N 8

/* Задаем необходимую точность приближения корней

*/

#define E 0.00001

/* Задаем массивы исходных и промежуточных данных

*/

static double A[N][M], F[N], Xk[N], Zk[N], Pk[M];

402

static double Rk[N],Rkp[M],Sr[N],alf,bet, mf, mfp; static double Spr1, Spr1p, Spp, Sppr, Sppp, Spprp;

int main(int argc, char **argv)

{int i, j, v, rank, size; int *index, *edges; MPI_Comm comm_gr; struct timeval tv1, tv2; long int dt1;

int reord = 1;

MPI_Init(&argc, &argv);

MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank);

index = (int *)malloc(size * sizeof(int)); edges=(int*)malloc(size*(size-1)*sizeof(int));

for(i = 0; i < size; i++)

{index[i] = (size - 1)*(i + 1);

v = 0;

for(j = 0; j < size; j++) { if(i != j)

edges[i * (size - 1) + v++] = j;

}

}

MPI_Graph_create(MPI_COMM_WORLD, size, index, edges, reord, &comm_gr);

/* Генерация данных */

for(mf=0,i = 0; i < N; i++)

{for(j = 0; j < M; j++)

{if((N*rank + i) == j)

A[i][j] = 2.0; else

A[i][j] = 1.0;

}

F[i] = M + 1; mf += F[i] * F[i];

403

}

/* Находим ||f|| у всех ветвей. */ MPI_Allreduce(&mf,&mfp,1,MPI_DOUBLE,MPI_SUM,

MPI_COMM_WORLD);

/* Каждая параллельная ветвь задает начальное приближение xk, rk, zk. */

for(i = 0; i < M; i++) Rkp[i] = 0.2;

for(i = 0; i < N; i++) Xk[i] = 0.2;

for(i = 0; i < N; i++)

{ for(Sr[i]=0,j = 0; j < M; j++) Sr[i] += A[i][j] * Rkp[j];

Rk[i] = F[i] - Sr[i]; Zk[i] = Rk[i];

}

MPI_Allgather(Zk, N, MPI_DOUBLE, Rkp, N, MPI_DOUBLE, comm_gr);

for(i = 0; i < N; i++)

{ for(Pk[i]=0,j = 0; j < M; j++) Pk[i] += A[i][j] * Rkp[j];

}

gettimeofday(&tv1, NULL);

/* Начало вычислений. Главный цикл. */ do

{

Spp = 0; Sppr = 0;

for(i = 0; i < N; i++)

{ Spp += Pk[i] * Pk[i]; Sppr += Pk[i] * Rk[i];

}

MPI_Allreduce(&Spp, &Sppp, 1, MPI_DOUBLE, MPI_SUM, comm_gr);

MPI_Allreduce(&Sppr, &Spprp, 1, MPI_DOUBLE, MPI_SUM, comm_gr);

404

alf = Spprp/Sppp;

Spr1 = 0;

for(i = 0; i < N; i++)

{Xk[i] += alf*Zk[i]; Rk[i] -= alf*Pk[i];

Spr1 += Rk[i]*Rk[i];

}

MPI_Allreduce(&Spr1, &Spr1p, 1, MPI_DOUBLE, MPI_SUM, comm_gr);

MPI_Allgather(Rk, N, MPI_DOUBLE, Rkp, N,

MPI_DOUBLE, comm_gr);

for(Sppr=0,i = 0; i < N; i++)

{ for(Sr[i]=0, j = 0; j < M; j++) Sr[i] += A[i][j] * Rkp[j];

Sppr += Pk[i] * Sr[i];

}

MPI_Allreduce(&Sppr, &Spprp, 1, MPI_DOUBLE, MPI_SUM, comm_gr);

bet = Spprp/Sppp;

for(i = 0;

i < N; i++)

 

{

Zk[i]

=

Rk[i]

+

bet * Zk[i];

 

Pk[i]

=

Sr[i]

+

bet

* Pk[i];

}

 

 

 

 

 

 

}

while(Spr1p/mfp > E*E);

/* конец основного цикла */

/* Все ветви засекают время и его значение выводят на монитор */

gettimeofday(&tv2, NULL);

dt1 = (tv2.tv_sec - tv1.tv_sec) * 1000000 + tv2.tv_usec - tv1.tv_usec;

printf("rank = %d Time = %d\n", rank, dt1);

/* Все ветви печатают, для контроля, свои первые восемь значений корней */

405

printf("rank=%d %f %f %f %f %f %f %f %f\n",rank, Xk[0],Xk[1],Xk[2],Xk[3],Xk[4],Xk[5],Xk[6],Xk[7]);

/* Все ветви завершают вычисления */

MPI_Finalize(); return(0);

}

406

8.3.Программирование на суперкомпьютерах с общей памятью и примеры параллельных программ в OpenMP

В этой части приводятся примеры параллельных алгоритмов решения задач на вычислительных системах с общей памятью с использованием системы параллельного программирования OpenMP. OpenMP в настоящее время является основной системой, ориентированным на параллельные вычисления над общей памятью. Приведенные здесь параллельные программы решения задач являются иллюстрационными, демонстрирующими способы распараллеливания задач подобного класса, и показывающими применение и возможности директив OpenMP. Рассматриваемые алгоритмы задач распараллеливаются, в основном, среднезернистыми методами.

OpenMP имеет явную (не автоматическую) модель программирования, предлагая программисту полное управление по распараллеливанию. В рамках данной технологии директивы параллелизма используются для выделения в программе

параллельных областей (parallel regions), в которых последовательный исполняемый код может быть разделен на несколько раздельных командных потоков (threads). Далее эти потоки могут исполняться на разных процессорах вычислительной системы. В результате такого подхода программа представляется в виде набора последовательных

(однопотоковых) и параллельных (многопотоковых) участков программного кода (см. рис. 3.1). Подобный принцип организации параллелизма получил наименование "вилочного" (Fork-Join) или пульсирующего параллелизма. Более полная информация по технологии OpenMP может быть получена, например, в информационных ресурсах сети Интернет [21].

Fork (ВЕТВЛЕНИЕ): главный поток создает группу параллельных потоков.

Код программы, который включен в параллельную конструкцию, будет выполняться параллельно в различных потоках.

407

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]