
- •ФіЛабораторна робота № 2
- •1. Теоретичні відомості
- •1.1 Термінологія і позначення
- •1.2 Загальні процедури mpi
- •1.3 Прийом/передача повідомлень із блокуванням (синхронні процедури)
- •1.4 Прийом/передача повідомлень без блокування (асинхронні процедури)
- •1.4 Визначені константи типу елементів повідомлень
- •2. Хід роботи
- •4. Контрольні питання
- •5. Список літератури
1.4 Визначені константи типу елементів повідомлень
-
Константи MPI
Тип в C
MPI_CHAR
signed char
MPI_SHORT
signed int
MPI_INT
signed int
MPI_LONG
signed long int
MPI_UNSIGNED_CHAR
unsigned char
MPI_UNSIGNED_SHORT
unsigned int
MPI_UNSIGNED
unsigned int
MPI_UNSIGNED_LONG
unsigned long int
MPI_FLOAT
float
MPI_DOUBLE
double
MPI_LONG_DOUBLE
long double
Інші визначені типи
MPI_Status - структура; атрибути повідомлень; містить три обов'язкові поля:
MPI_Source (номер процесу відправника)
MPI_Tag (ідентифікатор повідомлення)
MPI_Error (код помилки)
MPI_Request - системний тип; ідентифікатор операції посилки-прийому повідомлення
MPI_Comm - системний тип; ідентифікатор групи (комунікатора)
MPI_COMM_WORLD - зарезервований ідентифікатор групи, що складає їхніх всіх процесів програми
Константи-заглушки
MPI_COMM_NULL
MPI_DATATYPE_NULL
MPI_REQUEST_NULL
Константа невизначеного значення
MPI_UNDEFINED
Глобальні операції
MPI_MAX
MPI_MIN
MPI_SUM
MPI_PROD
Будь-який процес/ідентифікатор
MPI_ANY_SOURCE
MPI_ANY_TAG
Код успішного завершення процедури
MPI_SUCCESS
2. Хід роботи
Уважно ознайомтеся із теоретичною частиною лабораторної роботи.
Уважно ознайомтеся з наступним кодом програми, щоб зрозуміти принципи написання паралельних програм з використанням МРІ.
#include <stdio.h>
#include "mpi.h"
/*
* Початок і завершення:
* MPI_Init, MPI_Finalize
* Функції визначення кількості завдань у програмі й свого порядкового номера:
* MPI_Comm_size, MPI_Comm_rank
*/
int main( int argc, char **argv )
{
int size, rank, i;
/* Ініціалізуємо бібліотеку */
MPI_Init( &argc, &argv );
/* Визначаємо кількість завдань у запущеному додатку */
MPI_Comm_size( MPI_COMM_WORLD, &size );
/* ... і свій власний номер: від 0 до (size-1) */
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
/* завдання з номером 0 повідомляють користувача розмір групи,
* до якої прикріплена область зв'язку,
* до якої прикріплений описувач (комунікатор) MPI_COMM_WORLD,
* тобто число процесів у додатку!!
*/
if( rank==0 )
printf("Total processes count = %d\n", size );
/* Кожне завдання виводить користувачеві свій номер */
printf("Hello! My rank in MPI_COMM_WORLD = %d\n", rank );
/* Точка синхронізації, потім завдання 0 друкує
* аргументи командного рядка. У командному рядку
* можуть бути параметри, що додаються завантажником MPIRUN.
*/
MPI_Barrier( MPI_COMM_WORLD );
if( rank == 0 )
for( puts("Command line of process 0:"), i=0; i<argc; i++ )
printf( "%d: \"%s\"\n", i, argv[i] );
/* Всі завдання завершують виконання */
MPI_Finalize();
return 0;
}
Результат виконання програми
Total processes count = 11
Hello! My rank in MPI_COMM_WORLD = 0
Command line of process 0:
0: "C:\WMPI1.3\EXAMPLES\C\TEST\DEBUG\CONSOL~1.EXE"
1: "argument 0"
Hello! My rank in MPI_COMM_WORLD = 1
Hello! My rank in MPI_COMM_WORLD = 7
Hello! My rank in MPI_COMM_WORLD = 2
Hello! My rank in MPI_COMM_WORLD = 9
Hello! My rank in MPI_COMM_WORLD = 8
Hello! My rank in MPI_COMM_WORLD = 6
Hello! My rank in MPI_COMM_WORLD = 3
Hello! My rank in MPI_COMM_WORLD = 5
Hello! My rank in MPI_COMM_WORLD = 10
Hello! My rank in MPI_COMM_WORLD = 4
Наберіть код програми без коментарів (коментарі – усе що знаходиться в між /*....*/) у текстовому редакторі, відкомпілюйте та виконайте на віддаленому комп’ютері, так як було показано у Лабораторній роботі №1. Команди компіляції та запуску: mpicc та mpirun. Кількість паралельних процесів при виконанні вкажіть відповідно до вашого варіанту (Таблиця 2.1). Результат виконання програми скопіюйте у звіт.
Таблиця 2.1 – Варіанти завдань
№ в журналі |
Кількість паралельних процесів |
buffer (значення, що пересилається) |
Тип даних |
tag (ідентифікатор повідомлення) |
1 |
2 |
"a" |
MPI_CHAR |
908 |
2 |
3 |
8373 |
MPI_SHORT |
458 |
3 |
4 |
5977 |
MPI_INT |
460 |
4 |
5 |
753922 |
MPI_LONG |
541 |
5 |
6 |
253 |
MPI_UNSIGNED_CHAR |
744 |
6 |
7 |
50949 |
MPI_UNSIGNED_SHORT |
657 |
7 |
8 |
100 |
MPI_UNSIGNED |
611 |
8 |
2 |
200 |
MPI_UNSIGNED_LONG |
991 |
9 |
3 |
82,96 |
MPI_FLOAT |
171 |
10 |
4 |
19,7 |
MPI_DOUBLE |
789 |
11 |
5 |
580,3 |
MPI_LONG_DOUBLE |
685 |
12 |
6 |
"b" |
MPI_CHAR |
567 |
13 |
7 |
1475 |
MPI_SHORT |
399 |
14 |
8 |
7931 |
MPI_INT |
574 |
15 |
2 |
7669441 |
MPI_LONG |
261 |
16 |
3 |
215 |
MPI_UNSIGNED_CHAR |
358 |
17 |
4 |
47684 |
MPI_UNSIGNED_SHORT |
406 |
18 |
5 |
90 |
MPI_UNSIGNED |
784 |
19 |
6 |
120 |
MPI_UNSIGNED_LONG |
569 |
20 |
7 |
69,26 |
MPI_FLOAT |
922 |
21 |
8 |
442,4 |
MPI_DOUBLE |
993 |
22 |
2 |
9053,2 |
MPI_LONG_DOUBLE |
752 |
23 |
3 |
"c" |
MPI_CHAR |
575 |
24 |
4 |
1964 |
MPI_SHORT |
759 |
25 |
5 |
2459 |
MPI_INT |
761 |
26 |
6 |
339017 |
MPI_LONG |
188 |
27 |
7 |
244 |
MPI_UNSIGNED_CHAR |
234 |
28 |
8 |
33525 |
MPI_UNSIGNED_SHORT |
248 |
29 |
2 |
95 |
MPI_UNSIGNED |
621 |
30 |
3 |
138 |
MPI_UNSIGNED_LONG |
144 |
2.3 Уважно ознайомтеся з наступним кодом програми, щоб зрозуміти спосіб пересилки повідомлень між процесами. Наберіть код програми без коментарів у текстовому редакторі. Відкомпілюйте програму та виконайте на двох процесорах. Результат виконання програми скопіюйте у звіт.
#include <stdio.h>
#include "mpi.h"
/* Ідентифікатори повідомлень */
#define tagFloatData 1
#define tagDoubleData 2
/* Цей макрос створений для зручності, */
/* він дозволяє вказувати довжину масиву в кількості комірок*/
#define ELEMS(x) ( sizeof(x) / sizeof(x[0]) )
int main( int argc, char **argv )
{
int size, rank, count;
float floatData[10];
double doubleData[20];
MPI_Status status;
/* Ініціюємо бібліотеку */
MPI_Init( &argc, &argv );
/* Визначаємо кількість завдань у запущеному додатку */
MPI_Comm_size( MPI_COMM_WORLD, &size );
/* ... і свій власний номер: від 0 до (size-1) */
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
/* користувач повинен запустити рівно два завдання, інакше буде помилка */
if( size != 2 ) {
/* завдання з номером 0 повідомляють користувача про помилку */
if( rank==0 )
printf("Error: two processes required instead of %d, abort\n",
size );
/* Всі завдання-абоненти коммунікатора MPI_COMM_WORLD
* будуть очыкувати, поки завдання 0 не виведе повідомлення.
*/
MPI_Barrier( MPI_COMM_WORLD );
/* Без крапки синхронізації може виявитися, що одне із завдань
* викличе MPI_Abort раніше, ніж встигне виконатись printf()
* у завданні 0, MPI_Abort негайно примусово завершить
* всі завдання й повідомлення виведене не буде
*/
/* всі завданння аварійно завершують роботу */
MPI_Abort(
MPI_COMM_WORLD, /* Комунікатор області зв'язку, на яку */
/* поширюється дія помилки */
MPI_ERR_OTHER ); /* Цілочисельний код помилки */
return -1;
}
if( rank==0 ) {
/* Завдання 0 щось таке передає завданню 1 */
MPI_Send(
floatData, /* 1) адреса переданого масиву */
5, /* 2) скільки: 5 осередків, тобто floatData[0]..floatData[4] */
MPI_FLOAT, /* 3) тип комірки */
1, /* 4) кому: завданню 1 */
tagFloatData, /* 5) ідентифікатор повідомлення */
MPI_COMM_WORLD ); /* 6) комунікатор області зв'язку, через яку */
/* відбувається передача */
/* і ще одна передача: дані іншого типу */
MPI_Send( doubleData, 6, MPI_DOUBLE, 1, tagDoubleData, MPI_COMM_WORLD );
} else {
/* Завдання 1 щось таке приймає від завдання 0 */
/* чекаємо повідомлення й поміщаємо дані, що прийшли, у буфер */
MPI_Recv(
doubleData, /* 1) адреса масиву, куди записується прийняте */
ELEMS( doubleData ), /* 2) фактична довжина прийнятих даних */
/* масиву в числі комірок */
MPI_DOUBLE, /* 3) повідомляємо MPI, що повідомлення, що прийшло, */
/* складається із чисел типу 'double' */
0, /* 4) від кого: від завдання 0 */
tagDoubleData, /* 5) очікуємо повідомлення з таким ідентифікатором */
MPI_COMM_WORLD, /* 6) комунікатор області зв'язку, через яку */
/* очікується прийом повідомлення */
&status ); /* 7) сюди буде записаний статус завершення прийому */
/* Обчислюємо фактично прийнята кількість даних */
MPI_Get_count(
&status, /* статус завершення */
MPI_DOUBLE, /* повідомляємо MPI, що повідомлення, що прийшло, */
/* складається із чисел типу 'double' */
&count ); /* сюди буде записаний результат */
/* Виводимо фактичну довжину прийнятого на екран */
printf("[DOUBLE] Received %d elems\n", count );
/* Аналогічно приймаємо повідомлення з даними типу float
* Зверніть увагу: завдання-приймач має можливість
* приймати повідомлення не в тім порядку, у якому вони
* відправлялися, якщо ці повідомлення мають різні ідентифікатори
*/
MPI_Recv( floatData, ELEMS( floatData ), MPI_FLOAT,
0, tagFloatData, MPI_COMM_WORLD, &status );
MPI_Get_count( &status, MPI_FLOAT, &count );
printf("[FLOAT] Received %d elems\n", count );
}
/* Обидва завдання завершують виконання */
MPI_Finalize();
return 0;
}
Результат виконання програми
[DOUBLE] Received 6 elems
[FLOAT] Received 5 elems
2.4 Розглянемо тепер дві програми, що виконують одну і ту ж задачу пересилки числового значення від одного процесу до іншого, однак перша програма з використанням синхронних процедур send/receive, а інша - асинхронних процедур isend/ireceive.
Наберіть код обох програм у текстовому редакторі та збережіть у вигляді вихідних програм на С. Змініть у програмах значення змінних tag=1234 (ідентифікатор повідомлення) та buffer=5678 (значення, що пересилається) відповідно до вашого варіанту (Таблиця 2.1). Врахуйте в програмі тип даних що передаються. Відкомпілюйте ці обидві програми та виконайте на двох процесорах.
Результат виконання обох програм скопіюйте у звіт. Проаналізуйте відмінності роботи кожної програми та занотуйте у звіт.
а) Пересилка повідомлення з використанням синхронних процедур.
#include <stdio.h>
#include "mpi.h"
#include <math.h>
int main(argc,argv)
int argc;
char *argv[];
{
int myid, numprocs;
int tag,source,destination,count;
int buffer;
MPI_Status status;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
tag=1234;
source=0;
destination=1;
count=1;
if(myid == source){
buffer=5678;
MPI_Send(&buffer,count,MPI_INT,destination,tag,MPI_COMM_WORLD);
printf("synchronous: processor %d sent %d\n",myid,buffer);
}
if(myid == destination){
MPI_Recv(&buffer,count,MPI_INT,source,tag,MPI_COMM_WORLD,&status);
printf("synchronous: processor %d got %d , message tag is %d\n", myid, buffer, tag);
}
MPI_Finalize();
}
б) Пересилка повідомлення з використанням асинхронних процедур.
#include <stdio.h>
#include "mpi.h"
#include <math.h>
int main(argc,argv)
int argc;
char *argv[];
{
int myid, numprocs;
int tag,source,destination,count;
int buffer;
MPI_Status status;
MPI_Request request;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
tag=1234;
source=0;
destination=1;
count=1;
request=MPI_REQUEST_NULL;
if(myid == source){
buffer=5678;
MPI_Isend(&buffer,count,MPI_INT,destination,tag,MPI_COMM_WORLD,&request);
}
if(myid == destination){
MPI_Irecv(&buffer,count,MPI_INT,source,tag,MPI_COMM_WORLD,&request);
}
MPI_Wait(&request,&status);
if(myid == source){
printf("asynchronous: processor %d sent %d\n",myid,buffer);
}
if(myid == destination){
printf("asynchronous: processor %d got %d, message tag is %d\n",myid,buffer,tag);
}
MPI_Finalize();
}
3. ЗМІСТ ЗВІТУ
Тема, мета лабораторної роботи
Короткі теоретичні відомості
Опис виконання лабораторної роботи згідно пунктів 2.2-2.4
Відповідь на одне контрольне запитання згідно номера варіанту (порядковий номер у списку журналу групи)
Висновки