Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Метод АТПО лр.doc
Скачиваний:
3
Добавлен:
01.07.2025
Размер:
561 Кб
Скачать
☆

Хід роботи

Розглянемо роботу паралельної MPI програми обчислення інтегралу ‘π’. MPI-програма 'π' є безпосередньою модифікацією початкового послідовного коду. Щоб уникнути зайвого ускладнення, число кроків як і раніше задаватиметься в програмі, а не вводитиметься і передаватиметься іншим процесам.

На початку програми підключається файл MPI, щоб визначити типи даних, константи і підпрограми MPI. Потім − стандартна трійка підпрограм, щоб ініціалізувати середовище MPI і зробити основні параметри (число процесів і ранг) доступними для програми.

#include "mpi.h"

static long num_steps = 100000;

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

{

int i, my_id, numprocs;

double x, pi, step, sum = 0.0 ;

step = 1.0/(double) num_steps ;

MPI_Init(&argc, &argv) ;

MPI_Comm_Rank(MPI_COMM_WORLD, &my_id) ;

MPI_Comm_Size(MPI_COMM_WORLD, &numprocs) ;

my_steps = num_steps/numprocs ;

for (i=my_id; i<num_steps; i+numprocs)

{

x = (i+0.5)*step;

sum += 4.0/(1.0+x*x);

}

sum *= step ;

MPI_Reduce(&sum, &pi, 1, MPI_DOUBLE, MPI_SUM, 0,

MPI_COMM_WORLD) ;

MPI_Finalize(ierr);

}

Після цього використовується стандартний прийом для розділення ітерацій циклу між набором процесів. Необхідно відзначити, що початкове і кінцеве значення лічильника циклу були змінені так, щоб лічильник циклу змінювався від ідентифікатора кожного процесу до числа ітерацій з кроком, що дорівнює числу процесів в групі. Це працює, оскільки ранг в MPI використовується як ідентифікатор і може приймати значення від нуля до числа процесів мінус один. По суті, в результаті цієї простої зміни ітерації циклу розподіляються шляхом циклічного перебору, начебто різним процесам здавали колоду карт.

Після завершення роботи кожного процесу він вносить свій внесок до суми, поміщаючи готову проміжну суму в змінну "sum", а потім виконується редукція за допомогою наступного виклику:

MPI_Reduce(&sum, &pi, 1, MPI_DOUBLE, MPI_SUM, 0,

MPI_COMM_WORLD) ;

Проміжна сума, "sum", використовується як буфер відправки, а змінна "pi" — як буфер прийому. Це значення буде доставлено процесу з рангом "0", який вказаний як шостий аргумент підпрограми MPI_Reduce. Буфер відправки містить одне значення типу MPI_DOUBLE з операцією накопичення підсумовування (MPI_SUM). І, нарешті, процеси, що беруть участь в цій редукції, використовують комунікатор MPI_COMM_WORLD.

Розглянемо програму MPI для обчислення подвійного інтеграла.

#include <stdio.h>

#include <stdlib.h>

#include "mpi.h"

//----------------- Ця функція обчислює значення функції 5*X^2*Y+2*Y^3

float Func ( float X , float Y )

{return ( 5*X*X*Y-2*Y*Y*Y );}

//------- Ця функція повертає випадкове число із діапазону від 0 до 1

double GetRandom ()

{ return (double) rand ()/RAND_MAX;}

//----------------- Ця функція обчислює інтеграл --------------------

float DoubleIntegrall ( int N , // Кількість відрізків інтегрування

float AX , float BX ,// Інтервал, з якого обираємо випадкове значення для X

float AY , float BY )// Інтервал, з якого обираємо випадкове значення для Y

{

float HX = BX-AX , HY = BY-AY ,x, y, f = 0;

int i;

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

{

x = (float) ( GetRandom ()*HX + AX );

y = (float) ( GetRandom ()*HY + AY );

f =+ Func ( x , y );

}

return ( HX*HY*f/N );

}

//------------------- MAIN -------------------------------

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

{

int My_Rank; /* ранг поточного процесу */

int NumProcs; /* загальне число процесорів */

double AX , AY; /* ліва межа інтервалу */

double BX , BY; /* права межа інтервалу */

int N; /* число точок розбиття */

double LenX; /*LenY; /* довжина відрізку інтегрування для поточного процесу*/

double Local_AX , Local_AY;/* ліва межа інтервалу для поточного процесу */

double Local_BX , Local_BY; /* права межа інтервалу для поточного процесу */

int Local_N; /* число точок розбиття для поточного процесу */

double Local_Res;/* значення інтеграла у поточному процесі */

double Result; /* результат інтегрування */

double WTime; /* час роботи програми */

/* Ініціалізація */

srand ( ( unsigned ) time ( NULL ) );

/* Почати роботу з MPI */

MPI_Init(&argc, &argv);

/* Отримати номер поточного процесу в групі всіх процесів */

MPI_Comm_rank(MPI_COMM_WORLD, &My_Rank);

/* Отримати загальну кількість запущених процесів */

MPI_Comm_size(MPI_COMM_WORLD, &NumProcs);

/* Отримати дані */

if (My_Rank == 0)

{

printf("Введіть початкове значення X: ");

scanf("%lf", &AX);

printf("Введіть кінцеве значення X: ");

scanf("%lf", &BX);

printf("Введіть початкове значення Y: ");

scanf("%lf", &AY);

printf("Введіть кінцеве значення Y: ");

scanf("%lf", &BY);

printf("Введіть кількість випадкових значень : ");

scanf("%d", &N);

}

/* Розсилаемо дані з процесу 0 іншим */

MPI_Bcast(&AX, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

MPI_Bcast(&BX, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

MPI_Bcast(&AY, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

MPI_Bcast(&BY, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

MPI_Bcast(&N, 1, MPI_INT, 0, MPI_COMM_WORLD);

/* Синхронізація процесів */

MPI_Barrier(MPI_COMM_WORLD);

/* Запускаємо таймер */

WTime = MPI_Wtime();

/* Обчислюємо відрізок інтегрування для поточного процеса */

LenX = (BX-AX)/NumProcs;

LenY = (BY-AY)/NumProcs;

Local_N = N/NumProcs;

Local_AX = AX + My_Rank*LenX;

Local_BX = Local_AX + LenX;

Local_AY = AY; // + My_Rank*LenY;

Local_BY = BY; //Local_AY + LenY;

/* Обчислити інтеграл на кожному процесі */

Local_Res = DoubleIntegrall (Local_N, Local_AX, Local_BX, Local_AY, Local_BY);

/* Скласти всі значення і передати процесу 0 */

MPI_Reduce(&Local_Res, &Result, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

/* Синхронізація процесів */

MPI_Barrier(MPI_COMM_WORLD);

/* Обчислюємо час роботи */

WTime = MPI_Wtime() - WTime;

/* Надрукувати відповідь */

if (My_Rank == 0)

{

printf("Integral X:[ %.2lf ; %.2lf ] Y:[ %.2lf ; %.2lf ] = %.8lf\n", AX, BX, AY, BY, Result);

printf("Working time: %.2lf seconds\n", WTime);

}

/* Закінчуємо роботу з MPI */

MPI_Finalize();

return 0;

}