Schimb de date folosind MPI. Lucrul cu biblioteca MPI folosind exemplul Intel® MPI Library. Interacțiuni colective ale proceselor. Manipulatorii de erori asociate cu comunicatorii

Adnotare: Prelegerea este dedicată luării în considerare Tehnologii MPI ca standard de programare paralelă pentru sistemele de memorie distribuită. Sunt luate în considerare principalele moduri de transmitere a datelor. Sunt introduse concepte precum grupuri de procese și comunicatori. Acoperă tipuri de date de bază, operațiuni punct la punct, operațiuni colective, operațiuni de sincronizare și măsurători de timp.

Scopul prelegerii: Prelegerea are ca scop studierea metodologiei generale de dezvoltare algoritmi paraleli.

Înregistrare video a prelegerii - (volum - 134 MB).

5.1. MPI: concepte de bază și definiții

Să luăm în considerare o serie de concepte și definiții care sunt fundamentale pentru standardul MPI.

5.1.1. Conceptul de program paralel

Sub program paralelîn cadrul MPI, înțelegem un set de executate simultan proceselor. Procesele pot rula mai departe diferite procesoare, dar mai multe procese pot fi localizate pe un procesor (în acest caz, acestea sunt executate în modul de partajare a timpului). În cazul extrem, un singur procesor poate fi folosit pentru a executa un program paralel - de regulă, această metodă este utilizată pentru a verifica inițial corectitudinea programului paralel.

Fiecare proces al unui program paralel este generat dintr-o copie a aceluiași cod de program ( Modelul SPMP). The codul programului, prezentat în formular program executabil, trebuie să fie disponibil în momentul lansării programului paralel pe toate procesoarele utilizate. Codul sursă pentru programul executabil este dezvoltat în limbajele algoritmice C sau Fortran folosind una sau alta implementare a bibliotecii MPI.

Numărul de procese și numărul de procesoare utilizate sunt determinate în momentul lansării programului paralel utilizând mediul de execuție a programului MPI și nu se pot modifica în timpul calculelor (standardul MPI-2 oferă posibilitatea schimbare dinamică numărul de procese). Toate procesele programului sunt numerotate secvenţial de la 0 la p-1, Unde p este numărul total de procese. Se numește numărul procesului rang proces.

5.1.2. Operațiuni de transfer de date

MPI se bazează pe operațiuni de transmitere a mesajelor. Printre funcțiile furnizate ca parte a MPI, există diferite se dublează (punct la punct) operaţii între două procese şi colectiv (colectiv) acţiuni de comunicare pentru interacţiunea simultană a mai multor procese.

Poate fi folosit pentru a efectua operații asociate moduri diferite transmisii, inclusiv sincrone, blocare etc. - luarea în considerare pe deplin a posibilelor moduri de transmisie se va efectua în subsecțiunea 5.3.

După cum sa menționat mai devreme, standardul MPI prevede necesitatea implementării majorității operațiunilor de bază de transfer colectiv de date - vezi subsecțiunile 5.2 și 5.4.

5.1.3. Conceptul de comunicatori

Procesele unui program paralel sunt combinate în grupuri. Sub comunicator MPI se referă la un obiect de serviciu special creat care combină un grup de procese și un număr de parametri suplimentari (context) utilizat la efectuarea operațiunilor de transfer de date.

De obicei, operațiunile de transfer de date pereche sunt efectuate pentru procese aparținând aceluiași comunicator. Operațiile colective sunt aplicate simultan tuturor proceselor comunicatorului. Ca urmare, specificarea comunicatorului de utilizat este obligatorie pentru operațiunile de transfer de date în MPI.

În timpul calculelor, pot fi create noi grupuri de procese și comunicatoare, iar grupurile existente de procese și comunicatoare pot fi șterse. Același proces poate aparține unor grupuri și comunicatori diferite. Toate procesele prezente în programul paralel sunt incluse în comunicatorul creat implicit cu identificatorul MPI_COMM_WORLD.

Dacă trebuie să transferați date între procese de la grupuri diferite este necesar să se creeze un comunicator global ( intercomunicator).

O discuție detaliată despre capacitățile MPI de a lucra cu grupuri și comunicatori va fi efectuată în subsecțiunea 5.6.

5.1.4. Tipuri de date

Când efectuați operațiuni de transmitere a mesajelor, trebuie să specificați datele care urmează să fie trimise sau primite în funcțiile MPI. tip date trimise. MPI conține set mare tipuri de bază date care coincid în mare măsură cu tipurile de date din limbajele algoritmice C și Fortran. În plus, MPI are capacitatea de a crea noi tipuri derivate date pentru mai precise şi descriere scurta conținutul mesajelor redirecționate.

O discuție detaliată despre capacitățile MPI pentru lucrul cu tipuri de date derivate va fi efectuată în subsecțiunea 5.5.

5.1.5. Topologii virtuale

După cum sa menționat mai devreme, operațiunile de transfer de date asociate pot fi efectuate între orice procese ale aceluiași comunicator și toate procesele comunicatorului iau parte la o operațiune colectivă. În acest sens, topologia logică a liniilor de comunicare între procese are structura unui graf complet (indiferent de prezența unor canalele fizice comunicarea între procesoare).

În același timp (și acest lucru a fost deja notat în Secțiunea 3), pentru prezentarea și analiza ulterioară a unui număr de algoritmi paraleli, este recomandabil să existe o reprezentare logică a retea de comunicatii sub forma anumitor topologii.

MPI are capacitatea de a reprezenta mai multe procese în formă grătare dimensiune arbitrară (vezi subsecțiunea 5.7). În acest caz, procesele limită ale rețelelor pot fi declarate învecinate și, prin urmare, pe baza rețelelor, structuri de tip torus.

În plus, MPI are instrumente pentru generarea de topologii logice (virtuale) de orice tip necesar. O discuție detaliată a capacităților MPI pentru lucrul cu topologii va fi efectuată în subsecțiunea 5.7.

Și, în sfârșit ultimul rând Note înainte de a începe revizuirea MPI:

  • Descrierile funcțiilor și toate exemplele de programe furnizate vor fi prezentate în limbajul algoritmic C; caracteristicile utilizării MPI pentru algoritmic Limba Fortran vor fi date în clauza 5.8.1,
  • Scurtă descriere a implementărilor disponibile ale bibliotecilor MPI și descriere generala Mediile de execuție a programului MPI vor fi discutate în secțiunea 5.8.2,
  • Prezentarea principală a capabilităților MPI va fi axată pe versiunea standard 1.2 ( MPI-1); proprietăți suplimentare versiunea standard 2.0 va fi prezentată în clauza 5.8.3.

Când începeți să studiați MPI, se poate observa că, pe de o parte, MPI este destul de complex - standardul MPI prevede prezența a mai mult de 125 de funcții. Pe de altă parte, structura MPI este atent gândită - dezvoltarea programelor paralele poate începe după luarea în considerare a doar 6 funcții MPI. Toate caracteristici suplimentare MPI poate fi stăpânit pe măsură ce complexitatea algoritmilor și programelor dezvoltate crește. Și anume, în acest stil - de la simplu la complex - întregul material educativ conform MPI.

5.2. Introducere în dezvoltarea programelor paralele folosind MPI

5.2.1. Bazele MPI

Să prezentăm setul minim necesar de funcții MPI, suficient pentru dezvoltarea unor programe paralele destul de simple.

5.2.1.1 Inițializarea și terminarea programelor MPI

Prima funcție numită MPI ar trebui să fie o funcție:

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

pentru a inițializa mediul de execuție a programului MPI. Parametrii funcției sunt numărul de argumente de pe linia de comandă și textul funcției în sine. Linie de comanda.

Ultima funcție apelată MPI trebuie să fie o funcție:

int MPI_Finalizare(void);

Ca rezultat, se poate observa că structura unui program paralel dezvoltat folosind MPI ar trebui să aibă următoarea formă:

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

Ar trebui notat:

  1. Fişier mpi.h conține definiții ale constantelor numite, prototipuri de funcții și tipuri de date ale bibliotecii MPI,
  2. Funcții MPI_InitȘi MPI_Finalizare sunt obligatorii și trebuie executate (și o singură dată) de către fiecare proces al programului paralel,
  3. Înainte de apel MPI_Init funcția poate fi utilizată MPI_Initialized pentru a determina dacă un apel a fost efectuat anterior MPI_Init.

Exemplele de funcții discutate mai sus oferă o idee despre sintaxa pentru denumirea funcțiilor în MPI. Numele funcției este precedat de prefixul MPI, urmat de unul sau mai multe cuvinte ale numelui, primul cuvânt din numele funcției începe cu caracter capital, cuvintele sunt separate printr-o liniuță de subliniere. Numele funcțiilor MPI, de regulă, explică scopul acțiunilor efectuate de funcție.

Ar trebui notat:

  • Comunicator MPI_COMM_WORLD, după cum sa menționat mai devreme, este creat implicit și reprezintă toate procesele programului paralel care se execută,
  • Rang obtinut folosind functia MPI_Comm_rank, este rangul procesului care a efectuat apelul la această funcție, adică. variabil ProcRank va lua valori diferite în diferite procese.

Lansarea unei aplicații MPI pe un cluster de calcul este posibilă numai prin intermediul sistemului procesare în lot sarcini. Pentru a simplifica lansarea și punerea în coadă a unui program paralel, este furnizat un script mpirun special. De exemplu, mpirun -np 20 ./first.exe va rula programul paralel first.exe pe 20 de procesoare, de exemplu. la 5 noduri. (Fiecare nod are 2 procesoare dual-core). Este de remarcat faptul că pentru a lansa modul executabil situat în directorul curent ($pwd), trebuie să specificați în mod explicit calea „./” Un număr de implementări MPI-1 oferă o comandă de lansare pentru programele MPI, care are forma mpirun<аргументы mpirun><программа><аргументы программы>

Separarea comenzii de lansare a programului de programul în sine oferă flexibilitate, în special pentru implementările în rețea și eterogene. Având un mecanism standard de lansare, de asemenea, extinde portabilitatea programelor MPI cu un pas mai departe la liniile de comandă și scripturile care le manipulează. De exemplu, un script pentru un set de programe de validare care rulează sute de programe poate fi un script portabil dacă este scris folosind un astfel de mecanism de lansare standard. Pentru a nu confunda comanda ``standard'' cu cea existentă în practică, care nu este standard și nu este portabilă între implementări, MPI a definit mpiexec în loc de mpirun.

În timp ce un mecanism de lansare standardizat îmbunătățește capacitatea de utilizare a MPI, gama de medii este atât de diversă (de exemplu, poate să nu existe nici măcar o interfață de linie de comandă) încât MPI nu poate impune un astfel de mecanism. În schimb, MPI definește comanda mpiexec run și recomandă, dar nu solicită, ca sfat pentru dezvoltatori. Totuși, dacă o implementare oferă o comandă numită mpiexec, aceasta trebuie să ia forma descrisă mai jos: mpiexec -n <программа>

va exista cel puțin o modalitate de a alerga<программу>cu MPI_COMM_WORLD inițial al cărui grup conține proceselor. Alte argumente pentru mpiexec pot fi dependente de implementare.

Exemplul 4.1 Rularea a 16 instanțe ale myprog pe mașina curentă sau implicită:

mpiexec -n 16 myprog

3. Scrieți un program calcul paralel integrala definita din funcția 2*(x+2*x*x/1200.0) în intervalul .

Metoda dreptunghiului din stânga

dublu f(dublu x)

(întoarce 2*(x+2*x*x/1200);) // iskomyi integral

int main(int argc,char **argv)

MPI_Status status;

MPI_Init(&argc,&argv);

MPI_Comm_rank(MPI_COMM_WORLD,&rank);

MPI_Comm_size(MPI_COMM_WORLD,&size);

int n=1000,i,d; // 1000 - uzly

float a=0, b=1, h=(b-a)/n,s=0,r=0; //a i b -nachalo i konec otrezka

if (rank!=size-1) // schitaut vse processy, krome poslednego

( pentru (i=rang*d; i<(rank+1)*d; i++) { s=s+h*f(a+i*h); }

MPI_Send(&s,1,MPI_FLOAT,size-1,1,MPI_COMM_WORLD);)

( pentru (i=0; i

( MPI_Recv(&s,1,MPI_FLOAT,i,1,MPI_COMM_WORLD, &status); r+=s; ) )

MPI_Finalizare();)

Surak

1. Arhitecturi de memorie partajată și distribuită.

Memoria partajată distribuită (DSM - Memoria partajată distribuită)

În mod tradițional, calculul distribuit se bazează pe un model de transmitere a mesajelor, în care datele sunt transmise de la procesor la procesor sub formă de mesaje. Apelurile de procedură de la distanță sunt de fapt același model (sau foarte apropiate). DSM este un spațiu de adrese virtual partajat de toate nodurile (procesoarele) unui sistem distribuit. Programele accesează datele în DSM aproape în același mod în care accesează datele din memoria virtuală a computerelor tradiționale. În sistemele cu DSM, datele se deplasează între memoriile locale ale diferitelor computere în același mod în care se deplasează între RAM și memoria externă a unui computer. Configurația de memorie distribuită distribuită este o variantă a memoriei distribuite. Aici, toate nodurile, constând din unul sau mai multe procesoare conectate printr-o schemă SMP, utilizează un spațiu de adrese comun. Diferența dintre această configurație și o mașină cu memorie distribuită este că aici orice procesor poate accesa orice parte a memoriei. Cu toate acestea, timpul de acces pentru diferite secțiuni de memorie variază pentru fiecare procesor, în funcție de locul în care se află secțiunea fizică în cluster. Din acest motiv, astfel de configurații sunt numite și mașini cu acces neuniform la memorie (NUMA).

Diferențele dintre MPI și PVM.

Sistemul PVM (Mașină virtuală paralelă) a fost creat pentru a combina mai multe stații de lucru în rețea într-o singură mașină de calcul paralelă virtuală. Sistemul este un add-on la sistemul de operare UNIX și este utilizat pe diverse platforme hardware, inclusiv sisteme masiv paralele. Cele mai comune sisteme de programare paralelă astăzi se bazează pe MPI (Message Parsing Interface). Ideea MPI este inițial simplă și evidentă. Implică reprezentarea unui program paralel ca un set de procese de execuție paralele care interacționează între ele în timpul execuției transferului de date folosind proceduri de comunicare. Ei alcătuiesc biblioteca MPI. Cu toate acestea, implementarea corectă a MPI pentru a sprijini comunicațiile interprocesor s-a dovedit a fi destul de dificilă. Această complexitate este asociată cu necesitatea de a obține performanțe ridicate ale programului, nevoia de a utiliza numeroase resurse multicomputer și, ca urmare, o mare varietate în implementarea procedurilor de comunicare în funcție de modul de prelucrare a datelor.

Această notă arată cum să instalați MPI, să îl conectați la Visual Studio și apoi să îl utilizați cu parametrii specificați (numărul de noduri de calcul). Acest articol folosește Visual Studio 2015, deoarece... Acesta este cel cu care elevii mei au avut probleme (aceasta notă a fost scrisă de studenți pentru studenți), dar instrucțiunile vor funcționa probabil și pentru alte versiuni.

Pasul 1:
Trebuie să instalați HPC Pack 2008 SDK SP2 (în cazul dvs. poate exista deja o versiune diferită), disponibil pe site-ul oficial Microsoft. Capacitatea de biți a pachetului și a sistemului trebuie să se potrivească.

Pasul 2:
Trebuie să configurați căile; pentru a face acest lucru, accesați fila Depanare - Proprietăți:

„C:\Program Files\Microsoft HPC Pack 2008 SDK\Include”

În câmpul Directoare biblioteci:

„C:\Program Files\Microsoft HPC Pack 2008 SDK\Lib\amd64”

În câmpul bibliotecă, dacă există o versiune pe 32 de biți, trebuie să introduceți i386 în loc de amd64.

Msmpi.lib

:

Pasul 3:

Pentru a configura lansarea, trebuie să mergeți la fila Depanare și în câmpul Comandă specificați:

„C:\Program Files\Microsoft HPC Pack 2008 SDK\Bin\mpiexec.exe”

În câmpul Argumente de comandă, specificați, de exemplu,

N 4 $(TargetPath)

Numărul 4 indică numărul de procese.

Pentru a rula programul trebuie să conectați biblioteca

Calea către proiect nu trebuie să conțină chirilic. Dacă apar erori, puteți utiliza Microsoft MPI, disponibil pe site-ul web Microsoft.

Pentru a face acest lucru, după instalare, trebuie doar să introduceți calea în câmpul Comandă din fila Depanare:

„C:\Program Files\Microsoft MPI\Bin\mpiexec.exe”

De asemenea, înainte de a rula programul, nu uitați să indicați adâncimea de biți a acestuia:

Exemplu de rulare a unui program cu MPI:

#include #include folosind namespace std; int main(int argc, char **argv) ( int rang, dimensiune; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); cout<< "The number of processes: " << size << " my number is " << rank << endl; MPI_Finalize(); return 0; }

Rularea programului pe 2 noduri:

Funcții MPI

Tip derivat, operațiuni, tipuri de date

Bsend Buffer_attach Get_count ANY_SOURCE Sendrecv_replace ANY_TAG Sondă

Allgetherv Alltoall Alltoallv Reduce Rduce_scatter Scan

Un tip derivat este construit din tipuri MPI predefinite și tipuri derivate definite anterior folosind funcții speciale de constructor

MPI_Type_contiguous, MPI_Type_vector, MPI_Type_hvector, MPI_Type_indexed, MPI_Type_hindexed, MPI_Type_struct.

Un nou tip derivat este înregistrat prin apelarea funcției MPI_Type_commit. Numai după înregistrare un nou tip derivat poate fi utilizat în rutinele de comunicare și în construcția altor tipuri. Tipurile MPI predefinite sunt considerate înregistrate.

Când un tip derivat nu mai este necesar, acesta este distrus cu funcția MPI_Type_free.

1) MPI_Init - funcție de inițializare. Ca urmare a executării acestei funcții, este creat un grup de procese în care sunt plasate toate procesele de aplicație și este creată o zonă de comunicare, descrisă de comunicatorul predefinit MPI_COMM_WORLD.

MPI_Type_commit - tip înregistrare, MPI_Type_free - tip distrugere

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

2) MPI_Finalize - Funcție pentru finalizarea programelor MPI. Funcția închide toate procesele MPI și elimină toate zonele de comunicare.

int MPI_Finalize(void);

3) Funcție pentru determinarea numărului de procese din zona de comunicare MPI_Comm_size . Funcția returnează numărul de procese din zona de comunicare a comunicatorului comunicator.

int MPI_Comm_size(MPI_Comm comm, int *size);

4) Funcția de detectare a numărului de proces MPI_Comm_rank . Funcția returnează numărul procesului care a apelat această funcție. Numerele proceselor sunt în intervalul 0..size-1.

int MPI_Comm_rank(MPI_Comm comm, int *rank);

5) Funcția mesaj MPI_Send. Funcția trimite elemente de numărare a tipului de date mesaj cu etichetă de identificare pentru a procesa dest în zona de comunicare a comunicatorului comunicator.

int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);

6) Funcția de recepție a mesajelor MPI_Recv. Funcția primește elemente de numărare a tipului de date mesaj cu etichetă de identificare din procesul sursă în zona de comunicare a comunicatorului comunicator.

int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)

7) Funcția de sincronizare (temporizator) MPI_Wtime. Funcția returnează timpul astronomic în secunde care a trecut de la un anumit punct din trecut (punctul de referință).

dublu MPI_Wtime(void)

Funcțiile pentru transmiterea mesajelor între procese sunt împărțite în:

Prefix S (sincron)

înseamnă modul de transfer de date sincron. Operațiunea de transmitere a datelor se încheie numai când se încheie recepția datelor. Funcția este non-locală.

Prefixul B (buffered)

înseamnă modul de transfer de date tamponat. Un clipboard este creat în spațiul de adrese al procesului de trimitere folosind o funcție specială, care este utilizată în operațiunile de schimb. Operația de trimitere se termină când datele sunt plasate în acest buffer. Funcția este de natură locală.

Prefixul R (gata)

modul agreat sau pregătit de transmitere a datelor. Operația de transfer de date începe numai atunci când procesorul de recepție a setat semnul de pregătire pentru a primi date, inițiind operația de primire. Funcția este non-locală.

Prefixul I (imediat)

se referă la operațiuni neblocante.

Structura MPI_Status

După citirea unui mesaj, unii parametri pot fi necunoscuți, cum ar fi numărul de articole citite, ID-ul mesajului și adresa expeditorului. Aceste informații pot fi obținute folosind parametrul status. Variabilele de stare trebuie să fie declarate explicit în programul MPI. În limbajul C, starea este o structură de tip MPI_Status cu trei câmpuri MPI_SOURCE, MPI_TAG, MPI_ERROR.

8) Pentru a determina numărul de elemente de mesaj primite efectiv, trebuie să utilizați o funcție specială MPI_Get_count .

int MPI_Get_count (MPI_Status *status, MPI_Datatype datatype, int *count);

9) Puteți determina parametrii mesajului primit fără a-l citi folosind funcția MPI_Probe. int MPI_Probe (int source, int tag, MPI_Comm comm, MPI_Status *status);

10) În situațiile în care trebuie să faceți schimb de date între procese, este mai sigur să utilizați o operațiune combinată MPI_Sendrecv . În această operație, datele trimise din matricea buf sunt înlocuite cu datele primite.

int MPI_Sendrecv(void *sendbuf, int sendcount, MPI_Datatype sendtype, int dest, int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype, int source, MPI_Datatype recvtag, MPI_Comm comm, MPI_Status *status);

11) Funcție pentru verificarea finalizării unei operațiuni neblocante MPI_Test.

int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status);

Aceasta este o operațiune locală fără blocare. Dacă operațiunea asociată cu cererea s-a finalizat, este returnat flag = true, iar starea conține informații despre operația finalizată. Dacă operațiunea verificată nu s-a finalizat, se returnează flag = false, iar valoarea statusului este nedefinită în acest caz.

12) Funcție de anulare a unei cereri fără a aștepta finalizarea unei operațiuni neblocante MPI_Request_free.

int MPI_Request_free(MPI_Request *cerere);

Parametrul de solicitare este setat la MPI_REQUEST_NULL.

13) Realizarea execuției eficiente a unei operațiuni de transfer de date de la un proces la toate procesele unui program (difuzarea datelor) se poate realiza folosind funcția MPI:

int MPI_Bcast(void *buf,int count,MPI_Datatype type,int root,MPI_Comm comm)

Funcția MPI_Bcast difuzează date din bufferul tampon care conține elemente de numărare de tip de la un proces numerotat rădăcină către toate procesele incluse în comunicatorul de comunicații.

14) Dacă trebuie să primiți un mesaj de la cineva procesul de trimitere poate avea valoarea MPI_ANY_SOURCE specificată pentru parametrul sursă

15) Dacă este necesar să primiți un mesaj cu orice etichetă, valoarea poate fi specificată pentru parametrul etichetă MPI_ANY_TAG

16) Parametrul de stare vă permite să definiți un număr de caracteristici ale mesajului primit:

- status.MPI_SOURCE – rang procesul de trimitere a mesajului primit,

- status.MPI_TAG - eticheta mesajului primit.

17) Funcția

MPI_Get_coun t(MPI_Status *status, MPI_Datatype type, int *count)

returnează în variabila count numărul de elemente de tip tip din mesajul primit.

18) Operații care transferă date de la toate procesele la un singur proces. În această operațiune asupra colectate

valorile efectuează una sau alta prelucrare a datelor (pentru a sublinia ultimul punct, această operație se mai numește și operația de reducere a datelor)

int MPI_Reduce (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,int root,MPI_Comm comm)

19) Sincronizarea proceselor, de ex. realizarea simultană prin procese a anumitor puncte ale procesului de calcul se asigură cu ajutorul funcţiei MPI: int MPI_Barrier(MPI_Comm comm); Funcția MPI_Barrier definește o operație colectivă și, prin urmare, atunci când este utilizată, trebuie apelată de toate procesele comunicatorului utilizat. La apelarea funcției MPI_Barrier

execuția procesului este blocată; calculele procesului vor continua numai după ce toate procesele comunicatorului au apelat funcția MPI_Barrier.

20) Pentru a utiliza modul de transfer tamponat, trebuie creat și transferat un buffer de memorie MPI

pentru a tampona mesajele – funcția folosită pentru aceasta arată astfel: int MPI_Buffer_attach (void *buf, int size),

- buffer de memorie buf pentru memorarea mesajelor,

- dimensiune – dimensiunea tamponului.

21) După terminarea lucrului cu tamponul, acesta trebuie deconectat de la MPI utilizând funcția:

int MPI_Buffer_detach (void *buf, int *size).

22) Realizarea eficientă și garantată a execuției simultane a operațiunilor de transmisie și recepție a datelor se poate realiza folosind funcția MPI:

int MPI_Sendrecv (void *sbuf,int count,MPI_Datatype stype,int dest, int stag, void *rbuf,int rcount,MPI_Datatype

rtype,int source,int rtag, MPI_Comm comm, MPI_Status *status)

23) Când mesajele sunt de același tip, MPI are capacitatea de a utiliza un singur buffer: intMPI_Sendrecv_replace (void *buf, int count, MPI_Datatype type, int dest,

int stag, int source, int rtag, MPI_Comm comm, MPI_Status* status)

24) Operația generalizată de transmitere a datelor de la un proces către toate procesele (distribuția datelor) diferă de difuzare prin faptul că procesul transmite diferite date către procese (vezi Fig. 4.4). Această operație poate fi realizată folosind funcția:

int MPI_Scatter (void *sbuf,int scount,MPI_Datatype stype,

25) Operația de transfer generalizat de date de la toți procesoarele la un singur proces (colectarea datelor) este inversul procedurii de distribuție a datelor (vezi Fig. 4.5). Pentru a efectua această operație în MPI există o funcție:

int MPI_Gather (void *sbuf,int scount,MPI_Datatype stype,

void *rbuf,int rcount,MPI_Datatype rtype, int root, MPI_Comm comm)

26) Trebuie remarcat faptul că atunci când utilizați funcția MPI_Gather, colectarea datelor este efectuată numai

pe un proces. Pentru a obține toate datele colectate despre fiecare dintre procesele comunicatorului

trebuie să utilizați funcția de colectare și distribuție:

int MPI_Allgather (void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, MPI_Comm comm)

27) Transferul datelor de la toate procesele la toate procesele este cea mai comună operațiune de transfer de date (vezi Figura 4.6). Această operație poate fi realizată folosind funcția:

int MPI_Alltoall (void *sbuf,int scount,MPI_Datatype stype, void *rbuf,int rcount,MPI_Datatype rtype,MPI_Comm comm)

28) Funcția MPI_Reduce oferă rezultate de reducere a datelor

doar pe un proces. Pentru a obține rezultatele reducerii datelor pe fiecare dintre procesele comunicatorului, trebuie să utilizați funcția de reducere și distribuție:

int MPI_Allreduce (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,MPI_Comm comm).

29) Și o altă versiune a operațiunii de colectare și prelucrare a datelor, care asigură obținerea tuturor rezultatelor de reducere parțială, poate fi obținută folosind funcția:

int MPI_Scan (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,MPI_Comm comm).

Diagrama generală de execuție a funcției MPI_Scan este prezentată în Fig. 4.7. Elementele mesajelor primite reprezintă rezultatele prelucrării elementelor corespondente ale mesajelor transmise de procese și pentru a obține rezultate pe un proces cu rangul i, 0≤i

30) Valoarea inițială a variabilei bufpos trebuie să fie formată înainte de a începe ambalarea și apoi este setată de funcție MPI_Pack. Funcția MPI_Pack este apelată secvenţial pentru a împacheta toate datele necesare.

int MPI_Pack_size (int count, MPI_Datatype type, MPI_Comm comm, int *size)

31) După împachetarea tuturor datelor necesare, tamponul pregătit poate fi utilizat în funcțiile de transfer de date cu tipul MPI_PACKED specificat.

După primirea unui mesaj cu tipul MPI_PACKED, datele pot fi despachetate folosind funcția:

int MPI_Unpack (void *buf, int bufsize, int *bufpos, void *data, int count, tip MPI_Datatype, MPI_Comm comm)

Set de instrucțiuni complex Computer

CISC (calcularea setului de instrucțiuni complexe în engleză sau computerul setului de instrucțiuni complexe în engleză -

computer cu un set complet de instrucțiuni) este un concept de proiectare a procesorului care se caracterizează prin următorul set de proprietăți:

un număr relativ mic de registre de uz general;

· un număr mare de instrucțiuni de mașină, dintre care unele sunt încărcate similar din punct de vedere semantic cu operatorii limbajelor de programare de nivel înalt și sunt executate în multe cicluri de ceas;

· un număr mare de metode de adresare;

· un număr mare de formate de comandă de diferite dimensiuni de biți;

· predominanța formatului de comandă cu două adrese;

· prezența comenzilor de tip procesare registru-memorie.

Defecte:

cost ridicat al hardware-ului; dificultăţi de paralelizare a calculelor.

Tehnica de construcție a sistemului de instrucțiuni CISC este opusul unei alte tehnici - RISC. Diferența dintre aceste concepte constă în metodele de programare, nu în arhitectura actuală a procesorului. Aproape toate procesoarele moderne emulează atât seturile de instrucțiuni de tip RISC, cât și CISC.

Set de instrucțiuni redus Computer

Se bazează pe principiile arhitecturii RISC: format fix de instrucțiuni, operații de registru, execuție într-un singur ciclu a instrucțiunilor, metode simple de adresare și un fișier de registru mare. În același timp, există câteva caracteristici semnificative care disting această arhitectură de arhitecturile altor procesoare RISC. Acestea includ: un set independent de registre pentru fiecare dintre actuatori; includerea în sistem a instrucțiunilor individuale de tip CISC; lipsa unui mecanism de „tranziție întârziată”; un mod original de a implementa salturi condiționate. Principalele aplicații ale arhitecturilor cu microprocesoare sunt serverele și supercalculatoarele de înaltă performanță.

Astfel de computere se bazau pe o arhitectură care separa instrucțiunile de procesare de instrucțiunile de memorie și punea accent pe pipelining eficient. Sistemul de instrucțiuni a fost conceput în așa fel încât execuția oricărei instrucțiuni a luat un număr mic de cicluri de mașină (de preferință un ciclu de mașină). Logica în sine pentru executarea comenzilor pentru a crește performanța sa concentrat mai degrabă pe hardware decât pe implementarea firmware-ului. Pentru a simplifica logica de decodare a comenzilor, au fost folosite comenzi cu lungime fixă

Și format fix.

ÎN Care este scopul tehnologiei buffer-ului de adrese țintă de tranziție?

ÎN Procesorul oferă un mecanism pentru prezicerea dinamică a direcției tranzițiilor. Cu asta

Ținta de pe cip este o memorie cache mică numită branch target buffer (BTB) și două perechi independente de buffer-uri de prefatch de instrucțiuni (două buffer-uri de 32 de biți per conductă). Buffer-ul de adrese țintă al ramurilor stochează adresele instrucțiunilor care se află în bufferele de preluare preliminară. Funcționarea bufferelor de prefatch este organizată în așa fel încât, în orice moment dat, instrucțiunile să fie preluate numai într-unul dintre bufferele perechii corespunzătoare. Când o operațiune de ramificație este detectată în fluxul de instrucțiuni, adresa de ramificație calculată este comparată cu adresele stocate în BTB. Dacă există o potrivire, se preconizează că va avea loc ramificația și un alt buffer de preluare prealabilă este activat și începe să emită comenzi către conducta corespunzătoare pentru execuție. Dacă există o nepotrivire, se presupune că ramura nu va fi executată și bufferul de prefatch nu este comutat, continuând ordinea normală de emitere a comenzii. Acest lucru evită timpul de nefuncţionare a benzii transportoare

Conflicte structurale și modalități de a le minimiza

Modul combinat de execuție a comenzilor necesită, în general, pipeline de unități funcționale și duplicarea resurselor pentru a rezolva toate combinațiile posibile de comenzi din conductă. Dacă orice combinație de comenzi eșuează

fi acceptat din cauza conflictului de resurse, atunci se spune că mașina are un conflict structural. Cel mai tipic exemplu de mașini în care pot apărea conflicte structurale sunt mașinile cu dispozitive funcționale care nu sunt complet transportate.

Minimizare: conducta întrerupe execuția uneia dintre comenzi până când dispozitivul necesar devine disponibil.

Conflicte de date, opriri ale conductelor și implementarea mecanismului de ocolire

Unul dintre factorii care are un impact semnificativ asupra performanței sistemelor de transport este dependențele logice între instrucțiuni. Conflictele de date apar atunci când utilizarea procesării pipeline poate modifica ordinea apelurilor operanzilor, astfel încât această ordine să fie diferită de ordinea care este respectată atunci când instrucțiunile sunt executate secvențial pe o mașină non-pipeline. Problema prezentată în acest exemplu poate fi rezolvată folosind o tehnică hardware destul de simplă numită redirecționare a datelor, ocolire a datelor sau, uneori, scurtcircuitare.

Conflicte de date care duc la întreruperea conductei

În schimb, avem nevoie de hardware suplimentar, numit hardware interlook pipeline, pentru a ne asigura că exemplul rulează corect. În general, acest tip de echipament detectează conflicte și întrerupe conducta atâta timp cât există un conflict. În acest caz, acest hardware întrerupe conducta începând cu instrucțiunea care dorește să folosească datele, în timp ce instrucțiunea anterioară, al cărei rezultat este un operand pentru al nostru, produce acel rezultat. Acest echipament face ca o linie de producție să se blocheze sau să apară o „bulă” în același mod ca și în cazul conflictelor structurale.

Buffere de predicție de ramuri condiționate

Bufferul de predicție de ramificare condiționată este o memorie mică adresabilă de către biții mai puțin semnificativi ai adresei instrucțiunii de ramificare. Fiecare celulă din această memorie conține un bit, care indică dacă ramura anterioară a fost executată sau nu. Acesta este cel mai simplu tip de tampon de acest fel. Nu are etichete și este utilă doar pentru reducerea latenței ramurilor în cazul în care întârzierea este mai mare decât timpul necesar pentru a calcula valoarea adresei țintă a ramurilor. Bufferul de predicție al ramurilor poate fi implementat ca un mic cache dedicat accesat de adresa de instrucțiune în timpul etapei de preluare a instrucțiunii a conductei (IF), sau ca o pereche de biți asociați cu fiecare bloc de cache de instrucțiuni și preluați cu fiecare instrucțiune.

  • Tutorial

În această postare vom vorbi despre organizarea schimbului de date folosind MPI folosind exemplul Bibliotecii Intel MPI. Credem că aceste informații vor fi de interes pentru oricine dorește să se familiarizeze cu domeniul calculului paralel de înaltă performanță în practică.

Vom oferi o scurtă descriere a modului în care schimbul de date este organizat în aplicații paralele bazate pe MPI, precum și link-uri către surse externe cu o descriere mai detaliată. În partea practică, veți găsi o descriere a tuturor etapelor de dezvoltare a aplicației demo „Hello World” MPI, începând de la configurarea mediului necesar și terminând cu lansarea programului în sine.

MPI (Interfață de transmitere a mesajelor)

MPI este o interfață de transmitere a mesajelor între procesele care efectuează aceeași sarcină. Este destinat în primul rând sistemelor de memorie distribuită (MPP), spre deosebire de, de exemplu, OpenMP. Un sistem distribuit (cluster), de regulă, este un set de noduri de calcul conectate prin canale de comunicare de înaltă performanță (de exemplu, InfiniBand).

MPI este cel mai comun standard de interfață de date pentru programarea paralelă. Standardizarea MPI este realizată de Forumul MPI. Există implementări MPI pentru majoritatea platformelor, sistemelor de operare și limbilor moderne. MPI este utilizat pe scară largă în rezolvarea diferitelor probleme din fizica computațională, farmaceutică, știința materialelor, genetică și alte domenii de cunoaștere.

Din punct de vedere MPI, un program paralel este un set de procese care rulează pe diferite noduri de calcul. Fiecare proces este generat din același cod de program.

Operația principală în MPI este transmiterea mesajelor. MPI implementează aproape toate modelele de comunicare de bază: punct la punct, colectiv și unilateral.

Lucrul cu MPI

Să ne uităm la un exemplu viu al modului în care este structurat un program MPI tipic. Ca aplicație demonstrativă, să luăm exemplul de cod sursă care vine cu Biblioteca Intel MPI. Înainte de a rula primul nostru program MPI, trebuie să pregătim și să creăm un mediu de lucru pentru experimente.

Configurarea unui mediu cluster

Pentru experimente, vom avea nevoie de o pereche de noduri de calcul (de preferință cu caracteristici similare). Dacă nu aveți două servere la îndemână, puteți utiliza oricând serviciile cloud.

Pentru demonstrație, am ales serviciul Amazon Elastic Compute Cloud (Amazon EC2). Amazon oferă noilor utilizatori un an de probă gratuită de servere de nivel de intrare.

Lucrul cu Amazon EC2 este intuitiv. Dacă aveți întrebări, puteți consulta documentația detaliată (în engleză). Dacă doriți, puteți utiliza orice alt serviciu similar.

Creăm două servere virtuale funcționale. În consola de management, selectați Servere virtuale EC2 în cloud, apoi Lansați Instanța("Instanță" înseamnă o instanță de server virtual).

Următorul pas este selectarea sistemului de operare. Biblioteca Intel MPI acceptă atât Linux, cât și Windows. Pentru prima cunoaștere cu MPI, vom alege OS Linux. Alege Red Hat Enterprise Linux 6.6 pe 64 de biți sau SLES11.3/12.0.
Alege Tip de instanță(tip de server). Pentru experimente, t2.micro (1 vCPU, 2,5 GHz, familia de procesoare Intel Xeon, 1 GiB de RAM) este potrivit pentru noi. În calitate de utilizator recent înregistrat, aș putea folosi acest tip gratuit - marcat „Eligibil pentru nivelul gratuit”. Noi am stabilit Numărul de cazuri: 2 (număr de servere virtuale).

După ce serviciul ne solicită să rulăm Lansați Instanțele(servere virtuale configurate), salvăm cheile SSH care vor fi necesare pentru a comunica cu serverele virtuale din exterior. Starea serverelor virtuale și a adreselor IP pentru comunicarea cu serverele computerelor locale pot fi monitorizate în consola de management.

Punct important: în setări Rețea și securitate / Grupuri de securitate trebuie să creăm o regulă care va deschide porturi pentru conexiunile TCP - aceasta este necesară pentru managerul de proces MPI. Regula ar putea arăta astfel:

Tip: regulă TCP personalizată
Protocol: TCP
Interval de porturi: 1024-65535
Sursa: 0.0.0.0/0

Din motive de securitate, puteți seta o regulă mai strictă, dar pentru demonstrația noastră este suficient.

Și, în final, un scurt sondaj despre posibile subiecte pentru viitoarele publicații despre calculul de înaltă performanță.

Numai utilizatorii înregistrați pot participa la sondaj. , Vă rog.