Въведение в технологиите за паралелно програмиране (MPI). Основни функции на MPI

Случи се така, че трябваше да се занимавам отблизо с изучаването на паралелните изчисления и по-специално MPI. Може би тази посока е много обещаваща днес, така че бих искал да покажа на habrayuser основите на този процес.

Основни принципи и пример
Изчисляването на степента (e) ще бъде използвано като пример. Една от опциите за намирането му е серията Taylor:
e ^ x = ∑ ((x ^ n) / n!), където сумирането е от n = 0 до безкрайност.

Тази формула може лесно да бъде паралелизирана, тъй като необходимият брой е сумата от отделни термини и поради това всеки отделен процесор може да изчисли отделните термини.

Броят на термините, които ще бъдат изчислени във всеки отделен процесор, зависи както от дължината на интервала n, така и от наличния брой процесори k, които могат да участват в процеса на изчисление. Така например, ако дължината на интервала е n = 4 и в изчисленията участват пет процесора (k = 5), тогава от първия до четвъртия процесор ще получи по един член, а петият няма да бъде участващи. Ако n = 10 и k = 5, всеки процесор ще получи два члена за изчисляване.

Първоначално първият процесор използва функцията за излъчване MPI_Bcast, за да изпрати на останалите стойността на определената от потребителя променлива n. Като цяло функцията MPI_Bcast има следния формат:
int MPI_Bcast (void * buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm), където buffer е адресът на буфера с елемента, count е броят на елементите, datatype е съответният тип данни в MPI, root е рангът на доставката на основния процесор, а comm е името на комуникатора.
В моя случай, както вече споменахме, първият процесор с ранг 0 ще действа като основен процесор.

След това числото n ще бъде изпратено успешно, всеки процесор ще започне да изчислява своите условия. За да направите това, на всяка стъпка от цикъла числото i, което първоначално е равно на ранга на процесора, ще бъде допълнено с число, равно на броя на процесорите, участващи в изчисленията. Ако числото в следващите стъпки надвиши числото n, посочено от потребителя, изпълнението на цикъла за този процесор ще спре.

По време на изпълнението на цикъла термините ще бъдат добавени към отделна променлива и след приключването му получената сума ще бъде изпратена на главния процесор. За това ще се използва функцията MPI_Reduce на операцията за прехвърляне. Като цяло изглежда така:
int MPI_Reduce (void * buf, void * резултат, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)

Той обединява елементите на входния буфер на всеки процес в групата с помощта на операцията op и връща конкатенираната стойност към изходния буфер на номерирания корен на процеса. Резултатът от такава операция ще бъде една стойност, поради което функцията cast получи името си.

След изпълнение на програмата на всички процесори, първият процесор ще получи общата сума от термините, която ще бъде стойността на степента, от която се нуждаем.

Трябва да се отбележи, че както при паралелни, така и при последователни методи за изчисляване на степента, се използва рекурсивна функция за намиране на факториала. В хода на вземане на решение относно метода на паралелизиране на изпълняваната задача, разгледах варианта за намиране на факториала и на различни процесори, но в крайна сметка тази опция беше приета от мен като ирационална.

Основната задача все още е да се намери стойността на експонента и ако процесорите започнат да изчисляват всеки факториал на всеки член поотделно, това може да доведе до обратния ефект, а именно, значителна загуба на производителност и скорост на изчисление.
Това се обяснява с факта, че в този случай ще започне много голямо натоварване на комуникационната среда, което вече често е слабо звено в паралелните изчислителни системи. Ако изчисляването на факториала ще се извърши на всеки процесор по частен начин, натоварването на комуникационните линии ще бъде минимално. Този случай може да се нарече добър пример за факта, че проблемът с паралелизацията понякога трябва да има своите граници.

Алгоритъм за изпълнение на кода
1. Стойността на числото n се предава от визуалната обвивка към програмата, която след това се изпраща до всички процесори, използващи функцията за излъчване.
2. Когато се инициализира първият основен процесор, се стартира таймер.
3. Всеки процесор изпълнява цикъл, където приращението е броят на процесорите в системата. При всяка итерация на цикъла се изчислява член и сумата от тези термини се съхранява в променливата drobSum.
4. След края на цикъла всеки процесор добавя стойността на drobSum към променливата Result, използвайки функцията за прехвърляне MPI_Reduce.
5. След завършване на изчисленията на всички процесори, първият основен процесор спира таймера и изпраща получената стойност на променливата Result към изходния поток.
6. Стойността на времето, измерена от нашия таймер в милисекунди, също се изпраща към изходния поток.
Списък с кодове
Програмата е написана на C ++, ще приемем, че аргументите за изпълнение се предават от външната обвивка. Кодът изглежда така:
#включи "mpi.h"
#включи
#включи
използване на пространство от имена std;

двоен факт (int n)
{
ако (n == 0)
връщане 1;
друго
връщане n * Факт (n-1);
}

int main (int argc, char * argv)
{
SetConsoleOutputCP (1251);
int n;
int myid;
int numprocs;
int i;
int rc;
дълго двойно дроб, drobSum = 0, Резултат, сума;
двойно начално време = 0,0;
двойно крайно време;

N = atoi (argv);

if (rc = MPI_Init (& argc, & argv))
{
cout<< "Грешка при стартиране, изпълнението спря" << endl;
MPI_Abort (MPI_COMM_WORLD, rc);
}

MPI_Comm_size (MPI_COMM_WORLD, & numprocs);
MPI_Comm_rank (MPI_COMM_WORLD, & myid);

ако (myid == 0)
{

Startwtime = MPI_Wtime ();
}
MPI_Bcast (& n, 1, MPI_INT, 0, MPI_COMM_WORLD);

за (i = myid; i<= n; i += numprocs)
{
дроб = 1 / Факт (i);
drobSum + = дроб;
}

MPI_Reduce (& drobSum, & Result, 1, MPI_LONG_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
cout.precision (20);
ако (myid == 0)
{
cout<< Result << endl;
крайно време = MPI_Wtime ();
cout<< (endwtime-startwtime)*1000 << endl;
}

MPI_Финализиране ();
връщане на 0;
}


* Този изходен код беше подчертан с Source Code Highlighter.
Изход
Така получихме проста програма за изчисляване на експонента с помощта на няколко процесора наведнъж. Вероятно пречката е съхранението на самия резултат, тъй като с увеличаването на броя на цифрите няма да е тривиално да се съдържа стойността с помощта на стандартни типове и това място изисква доработка. Може би доста рационално решение е да запишете резултата във файл, въпреки че с оглед на чисто образователната функция на този пример, не можете да се съсредоточите специално върху това.

анотация: Лекцията е посветена на разглеждането на MPI технологията като стандарт за паралелно програмиране за системи с разпределена памет. Разгледани са основните начини на предаване на данни. Въвеждат се понятия като групи процеси и комуникатори. Обхванати са основни типове данни, операции от точка до точка, колективни операции, синхронизация и операции за измерване на времето.

Целта на лекцията:Лекцията е насочена към изучаване на общата методология за разработване на паралелни алгоритми.

Видеозапис на лекцията - (обем - 134 MB).

5.1. MPI: основни понятия и дефиниции

Нека разгледаме редица концепции и дефиниции, които са основни за стандарта MPI.

5.1.1. Концепция за паралелна програма

Под паралелна програмав рамките на MPI, набор от едновременно изпълнявани процеси... Процесите могат да се изпълняват на различни процесори, но няколко процеса могат да бъдат разположени и на един процесор (в този случай тяхното изпълнение се извършва в режим на споделяне на време). В краен случай може да се използва един процесор за изпълнение на паралелна програма - като правило този метод се използва за първоначална проверка на коректността на паралелна програма.

Всеки процес на паралелна програма се генерира въз основа на копие на същия програмен код ( SPMP модел). Този програмен код, представен под формата на изпълнима програма, трябва да е наличен в момента на стартиране на паралелната програма на всички използвани процесори. Изходният програмен код за изпълнимата програма е разработен на алгоритмични езици C или Fortran, използвайки една или друга реализация на библиотеката MPI.

Броят на процесите и броят на използваните процесори се определят в момента на стартиране на паралелна програма посредством средата за изпълнение на MPI програми и не могат да се променят в хода на изчисленията (стандартът MPI-2 предвижда възможност за динамична промяна броят на процесите). Всички процеси на програмата се преномерират последователно от 0 до р-1, където стрима общ брой процеси. Номерът на процеса е наименуван рангпроцес.

5.1.2. Операции по пренос на данни

MPI се основава на операции за предаване на съобщения. Сред функциите, предоставени в MPI, са различни сдвоени (от точка до точка) операции между два процеса и колективен (колективен) комуникационни действия за едновременно взаимодействие на няколко процеса.

За извършване на сдвоени операции могат да се използват различни режими на предаване, включително синхронен, блокиращ и т.н. - пълно разглеждане на възможните режими на предаванеще се извърши в подраздел 5.3.

Както беше отбелязано по-рано, стандартът MPI предвижда необходимостта от прилагане на повечето от основните операции за колективен трансфер на данни - вижте подраздели 5.2 и 5.4.

5.1.3. Концепцията за комуникатори

Процесите на паралелна програма се комбинират в група... Под комуникатор MPI означава специално създаден обект на услуга, който съчетава група процеси и редица допълнителни параметри ( контекст) се използва при извършване на операции по пренос на данни.

По правило операциите по сдвоен трансфер на данни се извършват за процеси, принадлежащи на един и същ комуникатор. Колективните операции се прилагат едновременно към всички комуникационни процеси. В резултат на това индикацията на използвания комуникатор е задължителна за операциите по пренос на данни в MPI.

В хода на изчисленията могат да се създават нови групи процеси и комуникатори и да се изтриват съществуващи групи процеси и комуникатори. Един и същ процес може да принадлежи на различни групи и комуникатори. Всички процеси, налични в паралелната програма, са част от комуникатора, създаден по подразбиране с идентификатора MPI_COMM_WORLD.

Ако е необходимо да се прехвърлят данни между процеси от различни групи, е необходимо да се създаде глобален комуникатор ( интеркомуникатор).

Подробно разглеждане на възможностите на MPI за работа с групи и комуникатори ще бъде направено в подраздел 5.6.

5.1.4. Типове данни

При извършване на операции по прехвърляне на съобщения, за да се посочат предадените или получени данни във функциите на MPI, е необходимо да се посочи типизпратени данни. MPI съдържа голям набор основни видоведанни, които до голяма степен съвпадат с типовете данни в алгоритмичните езици C и Fortran. В допълнение, MPI има способността да създава нови производни типоведанни за по-точно и кратко описание на съдържанието на препратените съобщения.

Подробно обсъждане на възможностите на MPI за работа с извлечени типове данни ще бъде направено в раздел 5.5.

5.1.5. Виртуални топологии

Както беше отбелязано по-рано, операциите за прехвърляне на данни в двойка могат да се извършват между всички процеси на един и същ комуникатор и всички процеси на комуникатора участват в колективната операция. В тази връзка логическата топология на комуникационните линии между процесите има структура на пълна графика (независимо от наличието на реални физически канали за комуникация между процесорите).

Наред с това (и това вече беше отбелязано в Раздел 3), за представяне и последващ анализ на редица паралелни алгоритми е препоръчително да се представи логически съществуващата комуникационна мрежа под формата на определени топологии.

MPI има способността да представя различни процеси във формата решеткапроизволно измерение (вижте подраздел 5.7). В този случай граничните процеси на решетките могат да бъдат декларирани за съседни и по този начин, на базата на решетките, структури от типа тор.

Освен това, MPI също има средства за формиране на логически (виртуални) топологии от всякакъв необходим тип. Подробно обсъждане на възможностите на MPI за работа с топологии ще бъде извършено в раздел 5.7.

И накрая, един последен набор от забележки, преди да започнете да разглеждате MPI:

  • Описанието на функциите и всички дадени примери за програми ще бъдат представени на алгоритмичен език C; спецификата на използването на MPI за алгоритмичния език на Fortran ще бъде дадена в раздел 5.8.1,
  • Кратко описание на наличните реализации на библиотеките на MPI и общо описание на средата за изпълнение на MPI програми ще бъдат разгледани в точка 5.8.2,
  • Основното представяне на възможностите на MPI ще бъде фокусирано върху стандарта версия 1.2 ( MPI-1); допълнителни свойства на стандарта версия 2.0 ще бъдат представени в точка 5.8.3.

Започвайки да изучавате MPI, може да се отбележи, че, от една страна, MPI е доста сложен - стандартът MPI предвижда наличието на повече от 125 функции. От друга страна, структурата на MPI е добре обмислена – разработването на паралелни програми може да започне след разглеждане само на 6 MPI функции. Всички допълнителни функции на MPI могат да бъдат усвоени с нарастването на сложността на разработените алгоритми и програми. Наречен в този стил - от просто до сложно - и по-нататък ще бъде представен целият учебен материал по MPI.

5.2. Въведение в разработването на паралелни програми с помощта на MPI

5.2.1. Основи на MPI

Ето минимално необходимия набор от MPI функции, достатъчен за разработването на сравнително прости паралелни програми.

5.2.1.1 Инициализация и прекратяване на MPI програми

Първата функция за извикване MPI трябва да бъде функция:

int MPI_Init (int * agrc, char *** argv);

за да инициализирате времето за изпълнение на програмата MPI. Параметрите на функцията са броят на аргументите в командния ред и текстът на самия команден ред.

Последната извикана функция MPI трябва да е функция:

int MPI_Finalize (недействителен);

В резултат на това може да се отбележи, че структурата на паралелна програма, разработена с помощта на MPI, трябва да бъде както следва:

#include "mpi.h" int main (int argc, char * argv) (<программный код без использования MPI функций>MPI_Init (& agrc, & argv);<программный код с использованием MPI функций>MPI_Финализиране ();<программный код без использования MPI функций>връщане на 0; )

Трябва да се отбележи:

  1. Файл mpi.hсъдържа дефиниции на именувани константи, прототипи на функции и типове данни на библиотеката MPI,
  2. Функции MPI_Initи MPI_Finalizeса задължителни и трябва да се изпълняват (и само веднъж) от всеки процес на паралелната програма,
  3. Преди да се обадите MPI_Initфункцията може да се използва MPI_Инициализиранза да определите дали е извършено повикване преди това MPI_Init.

Обсъдените по-горе примери за функции дават представа за синтаксиса за именуване на функции в MPI. Името на функцията е предшествано от MPI префикса, последвано от една или повече думи от името, първата дума в името на функцията започва с главни букви, думите са разделени с долно черта. Имената на функциите на MPI, като правило, обясняват целта на действията, извършвани от функцията.

Трябва да се отбележи:

  • комуникатор MPI_COMM_WORLD, както беше отбелязано по-рано, се създава по подразбиране и представлява всички процеси на паралелната програма, която се изпълнява,
  • Рангът, получен от функцията MPI_Comm_rank, е рангът на процеса, извършил извикването на тази функция, т.е. променлива ProcRankще придобие различни стойности в различните процеси.

Основни функции на MPI

Най-разпространената технология за програмиране за системи с паралелно разпределена памет в момента е MPI (Message Passing Interface). Основният начин, по който паралелните процеси взаимодействат един с друг в такива системи, е предаването на съобщения. По същество MPI е библиотека и време за изпълнение за паралелни програми на C или Fortran. Този урок ще опише примери за C програми.

Първоначално MPI позволява използването на модела за програмиране MIMD (Multiple Instruction Multiple Data) – множество потоци от инструкции и данни, т.е. комбиниране на различни програми с различни данни. Но програмирането за такъв модел на практика се оказва твърде сложно, затова обикновено се използва моделът SIMD (Single Program Multiple Data) – една програма и много потоци от данни. Тук се пише паралелна програма, така че различните нейни части да могат едновременно да изпълняват своята част от задачата, като по този начин се постига паралелизъм. Тъй като всички MPI функции се съдържат в библиотеката, ще е необходимо да свържете съответните модули при компилиране на паралелна програма.

В рамките на MPI паралелната програма се разбира като набор от едновременно изпълнявани процеси. Процесите могат да се изпълняват на различни процесори, но няколко процеса могат да бъдат разположени и на един процесор (в този случай тяхното изпълнение се извършва в режим на споделяне на време). Когато MPI програма се стартира в клъстер, всеки от нейните възли ще изпълни свое собствено копие на програмата, изпълнявайки своя част от задачата, от това следва, че паралелната програма е набор от взаимодействащи процеси, всеки от които работи в собственото си адресно пространство. В краен случай може да се използва един процесор за изпълнение на паралелна програма - като правило този метод се използва за първоначална проверка на коректността на паралелна програма.

Броят на процесите и броят на използваните процесори се определят в момента на стартиране на паралелна програма посредством средата за изпълнение на MPI - програмите и не могат да се променят в хода на изчисленията. Всички процеси в програмата са последователно номерирани от 0 до np-1, където np е общият брой процеси. Номерът на процеса се нарича ранг на процеса.

Паралелните процеси взаимодействат един с друг чрез изпращане на съобщения. Има два вида методи за изпращане (те се наричат ​​комуникация) - колективни и от точка до точка. При колективните комуникации процесът изпраща необходимата информация едновременно до цяла група процеси; има и по-общ случай, когато в рамките на група процеси информацията се предава от всеки процес към всеки. По-простите комуникации са комуникации от точка до точка, когато един процес изпраща информация на втория или и двамата обменят информация. Комуникационните функции са основните функции на библиотеката MPI. Освен това задължителните функции са функциите за инициализация и прекратяване на MPI - MPI_Init и MPI_Finalize. MPI_Init трябва да се извиква в самото начало на програмите, а MPI_Finalize трябва да се извиква в самия край. Всички други MPI функции трябва да бъдат извикани между тези две функции.

Как процесът знае коя част от изчислението трябва да извърши? Всеки процес, работещ в клъстер, има свой собствен уникален номер - ранг. Когато даден процес знае своя ранг и общия брой процеси, той може да определи своята част от работата. За това MPI има специални функции - MPI_Comm_rank и MPI_Comm_size. MPI_Comm_rank връща цяло число - ранга на процеса, който го е извикал, а MPI_Comm_size връща общия брой работещи процеси.

Процесите на паралелната потребителска програма, която се отстранява, са групирани. В MPI комуникаторът е специално създаден обект на услуга, който съчетава група процеси и редица допълнителни параметри (контекст), използвани при извършване на операции по пренос на данни. Комуникаторът, който се създава автоматично при стартиране на програмата и включва всички процеси в клъстера, се нарича MPI_COMM_WORLD. В хода на изчисленията могат да се създават нови групи процеси и комуникатори и да се изтриват съществуващи групи процеси и комуникатори. Един и същ процес може да принадлежи на различни групи и комуникатори. Колективните операции се прилагат едновременно за всички процеси на комуникатора, така че за тях един от параметрите винаги ще бъде комуникаторът.

При извършване на операции по прехвърляне на съобщения във функциите на MPI е необходимо да се посочи вида на данните, които се прехвърлят. MPI съдържа голям набор от основни типове данни, базирани на стандартните типове данни на езика C. В допълнение, програмистът може да конструира техните типове данни, използвайки специални функции на MPI. По-долу е дадена таблица на съответствието за основните типове данни.

MPI константи Тип данни на езика C
MPI_INT подписан вт
MPI_UNSIGNED unsigned int
MPI_SHORT подписан вт
MPI_LONG подписан long int
MPI_UNSIGNED_SHORT unsigned int
MPI_UNSIGNED_LONG unsigned long int
MPI_FLOAT плува
MPI_DOUBLE двойно
MPI_LONG_DOUBLE дълъг двоен
MPI_UNSIGNED_CHAR неподписан char
MPI_CHAR подписан char

Пример за стартиране на библиотеката на MPI: вход за студент, парола s304.

#включи

#включи

int main (int argc, char * argv)

/ * Инициализирайте MPI * /

MPI_Init (& argc, & argv);

/ * вземете ранга на процеса * /

MPI_Comm_rank (MPI_COMM_WORLD, & ранг);

/ * получаване на общия брой процеси * /

MPI_Comm_size (MPI_COMM_WORLD, & размер);

printf ("Здравей свят от процес% d от% d \ n", ранг, размер);

/ * прекратяване на MPI * /

Компилатор и линкер се използват за компилиране. Командна линия mpicc. (виж mpicc ... .- помощ)

Всеки от работещите процеси трябва да показва своя ранг и общия брой процеси. Нека се опитаме да компилираме и стартираме тази програма.

$ mpicc hello.c –o hello.o

$ mpicc hello.o –o здравей

Hello файлът ще бъде изпълнимият файл на примера. Можете да го стартирате на една машина и да видите, че броят на процесорите е 1, а рангът на процеса е 0:

$ ./здравей

Здравей свят от процес 0 от 1

Когато работите на сървъра, командата се използва за стартиране mpirun ... Той има два основни аргумента - името на файла, съдържащ адресите на възлите и броя на възлите, на които ще работи програмата.

$ mpirun n0-6 –v хостове здравей

Здравей свят от процес 0 от 7

Здравей свят от процес 3 от 7

Здравей свят от процес 5 от 7

Здравей свят от процес 4 от 7

Здравей свят от процес 2 от 7

Здравей свят от процес 6 от 7

Здравей свят от процес 1 от 7

Програмата ще работи на 7 възела (включително сървъра) и адресите на тези възли са в hosts файл.Печатът върху екрана се извършваше от процеси, които не са в реда на техните редици. Това се дължи на факта, че началото на процесите не е синхронизирано, но в MPI има специални функции за синхронизиране на процеси.

Най-простата програма не съдържа функции за съобщения. В реалните задачи процесите трябва да взаимодействат един с друг. Естествено, предаването на съобщения отнема време, което намалява коефициента на паралелизиране на задачата. Колкото по-висока е скоростта на интерфейса за съобщения (напр. Ethernet 10Mb/sec и Gigabit Ethernet), толкова по-ниски ще бъдат разходите за пренос на данни. Защото времето за обмен на данни между процесите е много (с порядък) по-дълго от времето за достъп до собствената му памет, разпределението на работата между процесите трябва да бъде „грубо“ и трябва да се избягват ненужните трансфери на данни.

Сред проблемите на числения анализ има много проблеми, чието успоредяване е очевидно. Например, численото интегриране всъщност се свежда до (многобройни) изчисления на интегралната функция (което е естествено да се повери на отделни процеси), докато основният процес контролира процеса на изчисление (определя стратегията за разпределение на точките на интеграция върху процесите и събира частични суми). Проблемите с търсене и сортиране в линеен списък, намиране на корените на функциите числено, търсене на екстремум на функция от много променливи, изчисляване на редове и други имат същата паралелизация. В тази лаборатория ще разгледаме два паралелни алгоритма за изчисляване на π.

Изчисляване на числото π по метода на численото интегриране

Известно е, че

Замествайки изчислението на интеграла с крайно сумиране, имаме , където n е броят на секциите за сумиране при числено интегриране. Площта на всеки участък се изчислява като произведение на ширината на „лентата“ от стойността на функцията в центъра на „ивицата“, след което площите се сумират от основния процес (еднаква мрежа е използван).

Очевидно паралелизирането на тази задача е лесно, ако всеки процес изчислява своята частична сума и след това предава резултата от изчислението на основния процес. Как да избегнем повтарящи се изчисления тук? Процесът трябва да знае своя ранг, общия брой процеси и броя на интервалите, на които ще бъде разделен сегментът (колкото повече интервали, толкова по-висока е точността). След това, в цикъл от 1 до броя на интервалите, процесът ще изчисли площта на лентата на i-тия интервал и след това ще премине не към следващия i + 1 интервал, а към интервала i + m , където m е броят на процесите. Както вече знаем, за да получите ранга и общия брой процеси, има функции MPI_Comm_rank и MPI_Comm_size ... Преди да започне изчисленията, основният процес трябва да прехвърли броя на интервалите към всички останали и след изчисленията да събере получените частични суми от тях и да ги сумира, в MPI това се реализира чрез изпращане на съобщения. Удобно е да използвате функцията за колективно взаимодействие за прехвърляне на съобщения тук. MPI_Bcast който изпраща едни и същи данни от един процес към всички останали. Има 2 варианта за събиране на частични суми - можете да използвате MPI_Gather , който събира данни от всички процеси и ги дава на един (получава се масив от m елемента, където m е броят на процесите) или MPI_Reduce . MPI_Reduce действа по подобен начин MPI_Gather - събира данни от всички процеси и ги дава на един, но не под формата на масив, а предварително извършва определена операция между елементите на масива, например сумиране и след това дава един елемент. За тази задача изглежда по-удобно за използване MPI_Reduce ... Текстът на програмата е даден по-долу

#include "mpi.h"

#включи

#включи

двойно f (двойно а)

връщане (4.0 / (1.0 + a * a));

int main (int argc, char * argv)

int n, myid, numprocs, i;

двоен PI25DT = 3,141592653589793238462643;

двойно mypi, pi, h, sum, x;

двойно начало, крайно време;

MPI_Init (& argc, & argv);

MPI_Comm_size (MPI_COMM_WORLD, & numprocs);

MPI_Comm_rank (MPI_COMM_WORLD, & myid);

startwtime = MPI_Wtime ();

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

h = 1,0 / (двоен) n;

за (i = myid + 1; i<= n; i += numprocs)

x = h * ((двойно) i - 0,5);

MPI_Reduce (& mypi, & pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

printf ("pi е приблизително% .16f, грешката е% .16f \ n",

pi, fabs (pi - PI25DT));

крайно време = MPI_Wtime ();

printf ("време на стенен часовник =% f \ n",

крайно време-начало);

Нека разгледаме по-отблизо извикванията на функции MPI_Bcast и MPI_Reduce :

MPI_Bcast (& n, 1, MPI_INT, 0, MPI_COMM_WORLD) - съдържанието на променлива n и един елемент от тип MPI_INT от процес с ранг 0 се изпраща към всички други процеси (MPI_COMM_WORLD - всички процеси в комуникатора) към същата променлива н. След това извикване всеки процес ще знае общия брой слотове. В края на изчисленията MPI_Reduce (& mypi, & pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD) сумира (параметър MPI_SUM) стойностите от променливите mypi от типа MPI_DOUBLE на всеки процес и записва резултата към процесната променлива pi с ранг 0. Процесът използва функция MPI_Wtime.

двойно MPI_Wtime ();

MPI_Wtimeвръща броя на секундите във формат с плаваща запетая, представляващ времето, изминало от началото на програмата.

Изчисляване на π по метода на Монте Карло

Методът „стреляне“ може да се използва за изчисляване на стойността на π. Приложен към този случай, методът се състои в генериране на точки, равномерно разпределени в двуизмерна област и определяне.

Изчислената по този начин стойност на π е приблизителна, в общия случай точността на изчисляване на желаната стойност се увеличава с увеличаване на броя на „изстрелите“ и качеството на генератора на случайни числа; подобни методи се използват в случай на трудности при точната числена оценка.

Паралелният алгоритъм за изчисляване на числото π по този метод е в много отношения подобен на предишния алгоритъм, който разгледахме. За да генерирате произволни числа, трябва да използвате функцията srand, която задава ранга на процеса като аргумент (зародата на последователността), така че всеки процес ще има своя собствена последователност.

#включи

void srand (неподписано начално число);

Функцията srand () задава началния номер за последователността, генерирана от функцията rand ().

#включи

int ранд (недействителен);

Функцията rand () генерира поредица от псевдослучайни числа. Всяко извикване на функцията връща цяло число между нула и стойността RAND_MAX.

За да съберете резултата, също така е удобно да използвате MPI_Reduce със спецификацията на операцията за сумиране (MPI_SUM), след което да разделите получената сума на броя на процесорите, като получите средноаритметичната стойност.

int MPI_Reduce (void * sendbuf, void * recvbuf, int count,

MPI_Datatype тип данни, MPI_Op op, int root, MPI_Comm comm);

Настроики:

sendbuf адрес на буфера за изпращане

recvbuf адрес на буфер за получаване

операция за намаляване

комуникационен комуникатор

int MPI_Bcast (void * буфер, int count, MPI_Datatype datatype, int root,

MPI_Comm comm);

Настроики:

буферен адрес на буфера за изпращане/получаване

брой елементи в буфера за изпращане (цяло число)

тип данни тип данни на елементите на буфера за изпращане

номер на основния процес (цяло число)

комуникационен комуникатор

Упражнение: в съответствие с номера на варианта, компилирайте и стартирайте паралелната програма, изчисляваща числото π според дадения алгоритъм.

Изпълнете задачата на един възел и от клъстер, на определен брой възли. Оценете времето за изчисление, точността и коефициента на успоредяване на Amdahl, като вземете предвид латентността на мрежата теоретично и въз основа на резултатите от работата.

Опции за работа

Вариант № Алгоритъм Брой процесори Брой повторения на всеки процесор
Числова интеграция
Монте Карло
Числова интеграция
Монте Карло
Числова интеграция
Монте Карло
Числова интеграция
Монте Карло
Числова интеграция
Монте Карло
Числова интеграция
Монте Карло
Числова интеграция
Монте Карло
Числова интеграция
Монте Карло
Числова интеграция
Монте Карло
Числова интеграция
Монте Карло

· Постановка на проблема, вариант.

· Текстът на паралелната програма на език C според задачата.

· Резултати от изпълнение на програмата на един възел, време за изпълнение t i, резултат от изчисление, грешка.

· Резултати от стартиране на програмата на сървъра, време на изпълнение, резултат от изчисление, грешка.

· Опишете паралелния алгоритъм, информационните потоци по време на изпълнение на програмата и зареждане на кеш паметта на възлите. Изчислете коефициента на Амдал - K j въз основа на резултатите от програмата.

· Като се вземат предвид резултатите от работата на група ученици, изградете хистограма на зависимостта на K j, t i от броя на процесорите, участващи в изчисленията.

  • Урок

В тази публикация ще говорим за организиране на обмен на данни с помощта на MPI, използвайки примера на Intel MPI Library. Смятаме, че тази информация ще представлява интерес за всеки, който иска да се запознае на практика с областта на паралелните високопроизводителни изчисления.

Ще предоставим кратко описание как е организиран обменът на данни в паралелни приложения, базирани на MPI, както и връзки към външни източници с по-подробно описание. В практическата част ще намерите описание на всички етапи от разработването на демонстрационното MPI приложение "Hello World", от настройката на необходимата среда до стартирането на самата програма.

MPI (интерфейс за предаване на съобщения)

MPI е интерфейс за предаване на съобщения между процеси, изпълняващи една и съща задача. Той е предназначен предимно за системи с разпределена памет (MPP), за разлика например от OpenMP. Разпределената (клъстерна) система, като правило, е набор от изчислителни възли, свързани с високопроизводителни комуникационни канали (например InfiniBand).

MPI е най-разпространеният стандарт за интерфейс за пренос на данни в паралелното програмиране. Стандартизацията на MPI се извършва от MPI Forum. Има MPI реализации за повечето съвременни платформи, операционни системи и езици. MPI се използва широко при решаване на различни проблеми на изчислителната физика, фармацевтиката, материалознанието, генетиката и други области на знанието.

Паралелна програма от гледна точка на MPI е набор от процеси, изпълнявани на различни изчислителни възли. Всеки процес се създава от един и същ програмен код.

Основната операция в MPI е предаването на съобщения. MPI прилага почти всички основни комуникационни модели: от точка до точка, колективни и едностранни.

Работа с MPI

Нека да разгледаме пример на живо как работи една типична MPI програма. Като демонстрационно приложение ще вземем изходния код на примера, предоставен с библиотеката Intel MPI. Преди да стартираме първата си програма MPI, трябва да подготвим и настроим работна среда за експерименти.

Създаване на клъстерирана среда

За експерименти се нуждаем от двойка изчислителни възли (за предпочитане с подобни характеристики). Ако нямате два сървъра под ръка, винаги можете да използвате облачни услуги.

За тази демонстрация избрах Amazon Elastic Compute Cloud (Amazon EC2). За новите потребители Amazon предоставя безплатна пробна година за сървъри от начално ниво.

Работата с Amazon EC2 е интуитивна. Ако имате въпроси, можете да се обърнете към подробната документация (на английски). Ако желаете, можете да използвате всяка друга подобна услуга.

Създаваме два работещи виртуални сървъра. В контролната конзола изберете EC2 виртуални сървъри в облака, тогава Стартирайте екземпляр(„Инстанция“ се отнася до екземпляр на виртуален сървър).

Следващата стъпка е да изберете операционната система. Intel MPI Library поддържа както Linux, така и Windows. За първо запознаване с MPI ще изберем OS Linux. Ние избираме Red Hat Enterprise Linux 6.6 64-битоваили SLES11.3 / 12.0.
Ние избираме Тип на екземпляра(тип сървър). За експерименти за нас е подходящ t2.micro (1 vCPU, 2,5 GHz, семейство процесори Intel Xeon, 1 GiB RAM). Като новорегистриран потребител бих могъл да използвам този тип безплатно - знакът "Free tier eligible". Настроихме Брой екземпляри: 2 (брой виртуални сървъри).

След като услугата ни подкани да стартираме Стартирайте екземпляри(конфигурирани виртуални сървъри), запазете SSH ключовете, които ще са необходими за комуникация с виртуални сървъри отвън. Състоянието на виртуалните сървъри и IP адресите за комуникация със сървърите на локалния компютър може да се следи в конзолата за управление.

Важен момент: в настройките Мрежа и сигурност / Групи за сигурностнеобходимо е да създадем правило, с което ще отваряме портове за TCP връзки - това е необходимо за мениджъра на процесите на MPI. Правилото може да изглежда така:

Тип: Персонализирано TCP правило
Протокол: TCP
Обхват на пристанището: 1024-65535
Източник: 0.0.0.0/0

От съображения за сигурност може да се посочи по-рестриктивно правило, но за нашата демонстрация това е достатъчно.

И накрая, малка анкета за възможни теми за бъдещи публикации за високопроизводителни компютри.

Само регистрирани потребители могат да участват в анкетата. , Моля те.