MPI aansluiten in Visual Studio. Voorbeelden uit het leerboek "Parallel Programming Technologies MPI en OpenMP"

Basis MPI-functies

De meest gebruikelijke programmeertechnologie voor parallelle systemen met gedistribueerd geheugen momenteel MPI (Message Passing Interface). De belangrijkste manier waarop parallelle processen in dergelijke systemen met elkaar communiceren, is het doorgeven van berichten. In wezen is MPI een bibliotheek- en uitvoeringsomgeving voor parallelle programma's in C of Fortran. IN deze handleiding Er zullen voorbeelden van programma's in C-taal worden beschreven.

Aanvankelijk staat MPI het gebruik van het MIMD-programmeermodel (Multiple Instruction Multiple Data) toe - vele stromen instructies en gegevens, d.w.z. Unie diverse programma's met diverse gegevens. Maar programmeren voor een dergelijk model blijkt in de praktijk te complex, daarom wordt meestal het SIMD-model (Single Program Multiple Data) gebruikt: één programma en veel datastromen. Hier wordt een parallel programma zo geschreven dat de verschillende delen tegelijkertijd hun deel van de taak kunnen uitvoeren, waardoor parallellisme wordt bereikt. Omdat alle MPI-functies bij het compileren in de bibliotheek aanwezig zijn parallel programma het zal nodig zijn om de overeenkomstige modules te koppelen.

In MPI wordt een parallel programma gedefinieerd als een reeks gelijktijdig uitgevoerde processen. Processen kunnen doorgaan verschillende verwerkers, maar er kunnen meerdere processen op één processor worden geplaatst (in dit geval worden ze uitgevoerd in de time-sharing-modus). Wanneer een MPI-programma op een cluster wordt uitgevoerd, zal elk van de knooppunten zijn eigen kopie van het programma uitvoeren en zijn eigen deel van de taak uitvoeren. Hieruit volgt dat een parallel programma een reeks op elkaar inwerkende processen is, die elk op zichzelf werken. adresruimte. In het extreme geval kan een enkele processor worden gebruikt om een ​​parallel programma uit te voeren - in de regel wordt deze methode gebruikt om in eerste instantie de juistheid van het parallelle programma te controleren.

Het aantal processen en het aantal gebruikte processors worden bepaald op het moment dat het parallelle programma wordt gestart met behulp van de MPI-programma-uitvoeringsomgeving en kunnen tijdens berekeningen niet veranderen. Alle processen van het programma zijn opeenvolgend genummerd van 0 tot np-1, waarbij np het totale aantal processen is. Het procesnummer wordt de procesrang genoemd.

Parallelle processen communiceren met elkaar door berichten te verzenden. Er zijn twee soorten verzendmethoden (ze worden communicatie genoemd): collectief en punt-tot-punt. Bij collectieve communicatie verzendt het proces Nodige informatie tegelijkertijd een hele groep processen, er zijn er nog meer algemeen geval, wanneer binnen een groep processen informatie van elk proces naar elk wordt overgedragen. Eenvoudigere communicatie is point-to-point-communicatie, waarbij het ene proces informatie naar het andere verzendt of beide informatie uitwisselen. Communicatiefuncties zijn de belangrijkste functies van de MPI-bibliotheek. Bovendien zijn de vereiste functies de MPI-initialisatie- en beëindigingsfuncties – MPI_Init en MPI_Finalize. MPI_Init moet helemaal aan het begin van programma's worden aangeroepen, en MPI_Finalize helemaal aan het einde. Alle andere MPI-functies moeten tussen deze twee functies worden aangeroepen.

Hoe weet een proces welk deel van de berekening het moet uitvoeren? Elk proces dat op het cluster wordt uitgevoerd, heeft zijn eigen proces uniek nummer– rang. Zodra een proces zijn rang en het totale aantal processen kent, kan het zijn aandeel in het werk bepalen. Voor dit doel heeft MPI speciale functies– MPI_Comm_rank en MPI_Comm_size. MPI_Comm_rank retourneert de rangorde van gehele getallen van het proces dat het heeft aangeroepen, en MPI_Comm_size retourneert het totale aantal actieve processen.

De processen van het parallelle gebruikersprogramma waarvan fouten worden opgespoord, worden in groepen gecombineerd. In MPI wordt een communicator opgevat als een speciaal gemaakt serviceobject dat een groep processen en een aantal combineert aanvullende parameters(context) gebruikt bij het uitvoeren van gegevensoverdrachtbewerkingen. Een communicator die automatisch wordt gemaakt wanneer het programma start en alle processen op het cluster omvat, wordt MPI_COMM_WORLD genoemd. Tijdens berekeningen kunnen nieuwe worden aangemaakt en verwijderd. bestaande groepen processen en communicatoren. Hetzelfde proces kan erbij horen verschillende groepen en communicatoren. Collectieve bewerkingen worden gelijktijdig toegepast op alle communicatorprocessen, dus voor hen zal een van de parameters altijd de communicator zijn.

Bij het uitvoeren van berichtendoorvoerbewerkingen in MPI-functies is het noodzakelijk om het type gegevens dat wordt verzonden op te geven. MPI bevat een grote set basistypen gegevens gebaseerd op standaard typen C-taalgegevens Bovendien kan de programmeur zijn eigen gegevenstypen samenstellen met behulp van speciale MPI-functies. Hieronder vindt u een toewijzingstabel voor basisgegevenstypen.

MPI-constanten Gegevenstype C-taal
MPI_INT ondertekend int
MPI_UNSIGNED niet ondertekend int
MPI_SHORT ondertekend int
MPI_LONG gesigneerd lange int
MPI_UNSIGNED_SHORT niet ondertekend int
MPI_UNSIGNED_LONG niet-ondertekend lange int
MPI_FLOAT vlot
MPI_DOUBLE dubbele
MPI_LONG_DOUBLE lange dubbele
MPI_UNSIGNED_CHAR Ongetekend char
MPI_CHAR ondertekende char

Voorbeeld van het starten van de MPI-bibliotheek: login student, wachtwoord s304.

#erbij betrekken

#erbij betrekken

int hoofd (int argc, char *argv)

/* MPI-initialisatie */

MPI_Init(&argc, &argv);

/* de procesrangschikking ophalen */

MPI_Comm_rank (MPI_COMM_WORLD, &rang);

/* haal het totale aantal processen op */

MPI_Comm_size (MPI_COMM_WORLD, &grootte);

printf(" Hallo Wereld van proces %d van %d\n", rang, grootte);

/* volledige MPI */

Voor het compileren worden een compiler en linker gebruikt. Opdrachtregel mpic. (zie mpicc….- help)

Elk van lopende processen moet de rangorde en het totale aantal processen weergeven. Laten we proberen dit programma te compileren en uit te voeren.

$ mpicc hallo.c –o hallo.o

$ mpicc hallo.o –o hallo

Het hallo-bestand zal zijn uitvoerbaar bestand voorbeeld. Je kunt het op één machine uitvoeren en zien dat het aantal processors gelijk is aan 1 en dat de procesrang 0 is:

$./hallo

Hallo wereld uit proces 0 van 1

Wanneer u op een server werkt, wordt het commando gebruikt om te starten mpirun . Het heeft twee hoofdargumenten: de naam van het bestand dat de knooppuntadressen bevat en het aantal knooppunten waarop het programma zal worden uitgevoerd.

$ mpirun n0-6 –v hosts hallo

Hallo wereld uit proces 0 van 7

Hallo wereld uit proces 3 van 7

Hallo wereld uit proces 5 van 7

Hallo wereld uit proces 4 van 7

Hallo wereld uit proces 2 van 7

Hallo wereld uit proces 6 van 7

Hallo wereld uit proces 1 van 7

Het programma draait op 7 knooppunten (inclusief de server) en de adressen van deze knooppunten zijn binnen hosts-bestand. Het afdrukken op het scherm werd uitgevoerd door processen die niet in hun gelederen stonden. Dit komt omdat het verloop van processen niet gesynchroniseerd is, maar MPI speciale functies heeft voor het synchroniseren van processen.

Het eenvoudigste programma bevat geen berichtfuncties. Bij echte problemen moeten processen met elkaar interacteren. Uiteraard wordt er tijd besteed aan het verzenden van berichten, waardoor de parallellisatiecoëfficiënt van de taak afneemt. Hoe hoger de snelheid van de interface voor berichtoverdracht (bijvoorbeeld 10 Mb/sec Ethernet en Gigabit Ethernet), hoe lager de kosten voor gegevensoverdracht zullen zijn. Omdat de tijd van gegevensuitwisseling tussen processen is veel (in ordes van grootte) groter dan de toegangstijd eigen geheugen moet de verdeling van het werk tussen processen 'grofkorrelig' zijn en moet onnodige gegevensoverdracht worden vermeden.

Onder de problemen van de numerieke analyse bevinden zich veel problemen waarvan de parallellisatie voor de hand ligt. Numerieke integratie komt bijvoorbeeld feitelijk neer op (talrijke) berekeningen van de integrandfunctie (die uiteraard is toevertrouwd aan individuele processen), terwijl het hoofdproces het berekeningsproces bestuurt (de strategie bepaalt voor het verdelen van integratiepunten over processen en deelsommen verzamelt) . Problemen met zoeken en sorteren in een lineaire lijst, het numeriek vinden van de wortels van functies, het zoeken naar extremen van een functie van veel variabelen, het berekenen van reeksen en andere hebben vergelijkbare parallellisatie. In deze laboratorium werk we zullen twee parallelle algoritmen bekijken voor het berekenen van het getal π.

Berekening van het getal π door middel van numerieke integratiemethode

Het is bekend dat

We hebben de berekening van de integraal vervangen door een eindige sommatie , waarbij n het aantal sommatiesecties is tijdens numerieke integratie. De oppervlakte van elke sectie wordt berekend als het product van de breedte van de 'strook' en de waarde van de functie in het midden van de 'strook', waarna de gebieden worden opgeteld door het hoofdproces (een uniform raster wordt gebruikt).

Het is duidelijk dat het parallelliseren van dit probleem gemakkelijk is als elk proces zijn deelsom berekent en vervolgens het resultaat van de berekening doorgeeft aan het hoofdproces. Hoe kan ik hier herhaalde berekeningen vermijden? Het proces moet zijn rang kennen, het totale aantal processen en het aantal intervallen waarin het segment zal worden verdeeld (hoe meer intervallen, hoe hoger de nauwkeurigheid). Vervolgens berekent het proces, in een cyclus van 1 tot het aantal intervallen, het gebied van de strip op het i-de interval en gaat dan niet naar het volgende i+1-interval, maar naar het i+m-interval , waarbij m het aantal processen is. Zoals we al weten, zijn er functies om de rangorde en het totale aantal processen te verkrijgen MPI_Comm_rang En MPI_Comm_grootte . Voordat met de berekeningen wordt begonnen, moet het hoofdproces het aantal intervallen naar alle anderen verzenden, en na de berekeningen de ontvangen deelsommen daarvan verzamelen en samenvatten in MPI. Dit wordt geïmplementeerd door berichten door te geven; Om berichten te verzenden is het handig om de functie te gebruiken collectieve interactie MPI_Bcast , waarmee dezelfde gegevens van het ene proces naar alle andere worden verzonden. Om gedeeltelijke bedragen te innen zijn er 2 opties die u kunt gebruiken MPI_Verzamelen , dat gegevens verzamelt van alle processen en deze aan één proces geeft (er wordt een array van m elementen verkregen, waarbij m het aantal processen is) of MPI_Verminderen . MPI_Verminderen gedraagt ​​zich op dezelfde manier MPI_Verzamelen – verzamelt gegevens van alle processen en geeft deze aan één proces, maar niet in de vorm van een array, maar voert eerst een bepaalde bewerking uit tussen de elementen van de array, bijvoorbeeld sommatie, en geeft dan één element. Voor deze taak lijkt het handiger in gebruik MPI_Verminderen . Hieronder vindt u de programmatekst

#include "mpi.h"

#erbij betrekken

#erbij betrekken

dubbele f(dubbele a)

rendement (4,0 / (1,0 + a*a));

int hoofd(int argc, char *argv)

int n, myid, numprocs, i;

dubbele PI25DT = 3,141592653589793238462643;

dubbele mypi, pi, h, som, x;

dubbele starttijd, eindtijd;

MPI_Init(&argc,&argv);

MPI_Comm_size(MPI_COMM_WORLD,&numprocs);

MPI_Comm_rank(MPI_COMM_WORLD,&mijnid);

startwtime = MPI_Wtime();

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

h = 1,0 / (dubbel) n;

voor (i = myid + 1; i<= n; i += numprocs)

x = h * ((dubbel)i - 0,5);

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

printf("pi is ongeveer %.16f, fout is %.16f\n",

pi, fabs(pi - PI25DT));

endwtime = MPI_Wtime();

printf("tijd wandklok = %f\n",

eindtijd-begintijd);

Laten we de functieaanroepen eens nader bekijken MPI_Bcast En MPI_Verminderen :

MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD) – de inhoud van variabele n en één element van het type MPI_INT van een proces met rang 0 wordt naar alle andere processen (MPI_COMM_WORLD – alle processen in de communicator) verzonden naar dezelfde variabele n . Na deze oproep kent elk proces het totale aantal intervallen. Aan het einde van de berekeningen telt MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD) de waarden op van de variabelen mypi van het type MPI_DOUBLE van elk proces en schrijft het resultaat naar het proces variabele pi met rang 0. Om de rekentijd te meten, gebruikt het hoofdproces de functie MPI_Wtime.

dubbele MPI_Wtime();

MPI_Wtijd retourneert het aantal seconden in drijvende-kommanotatie, die de tijd vertegenwoordigt die is verstreken sinds het programma is gestart.

Berekening van het getal π met behulp van de Monte Carlo-methode

Om de waarde van π te berekenen, kun je de 'schiet'-methode gebruiken. Wanneer toegepast op dit geval, bestaat de methode uit het genereren van punten die uniform verdeeld zijn over een tweedimensionaal gebied en het bepalen van .

De op deze manier berekende waarde van π is over het algemeen bij benadering; de nauwkeurigheid van het berekenen van de gewenste waarde neemt toe met het aantal ‘shots’ en de kwaliteit van de generator voor willekeurige getallen; Soortgelijke methoden worden gebruikt wanneer het moeilijk is een nauwkeurige numerieke schatting te maken.

Het parallelle algoritme voor het berekenen van het getal π met deze methode is in veel opzichten vergelijkbaar met het vorige algoritme dat we hebben overwogen. Om willekeurige getallen te genereren, moet u de srand-functie gebruiken, die de rangorde van het proces als argument (reekszaad) instelt, zodat elk proces zijn eigen reeks heeft.

#erbij betrekken

void srand(niet-ondertekend zaad);

De functie srand() stelt het startnummer in voor de reeks die wordt gegenereerd door de functie rand().

#erbij betrekken

int rand(nietig);

De functie rand() genereert een reeks pseudowillekeurige getallen. Elke keer dat de functie wordt aangeroepen, wordt een geheel getal tussen nul en de RAND_MAX-waarde geretourneerd.

Om hier het resultaat te verzamelen, is het ook handig om MPI_Reduce te gebruiken met een sommatiebewerking (MPI_SUM), en vervolgens de resulterende som te delen door het aantal processors, waardoor het rekenkundig gemiddelde wordt verkregen.

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

MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm);

Opties:

sendbuf-adres van de verzendende buffer

recvbuf-adres van de ontvangende buffer

op reductie operatie

com-communicator

int MPI_Bcast(ongeldig *buffer, int aantal, MPI_Datatype datatype, int root,

MPI_Comm comm);

Opties:

bufferadres van de zend-/ontvangstbuffer

tel het aantal elementen in de verzendbuffer (geheel getal)

datatype gegevenstype van verzendende bufferelementen

root hoofdprocesnummer (geheel getal)

com-communicator

Oefening: compileer en voer in overeenstemming met het optienummer een parallel programma uit dat het getal π berekent met behulp van een bepaald algoritme.

Voer een taak uit op één knooppunt en vanuit een cluster op een opgegeven aantal knooppunten. Evalueer de rekentijd, nauwkeurigheid en Amdahl-parallellisatiecoëfficiënt, rekening houdend met theoretische netwerkvertraging en op basis van de resultaten van het werk.

Taakopties

Optie nr. Algoritme Aantal processoren Aantal iteraties op elke processor
Numerieke integratie
Monte Carlo
Numerieke integratie
Monte Carlo
Numerieke integratie
Monte Carlo
Numerieke integratie
Monte Carlo
Numerieke integratie
Monte Carlo
Numerieke integratie
Monte Carlo
Numerieke integratie
Monte Carlo
Numerieke integratie
Monte Carlo
Numerieke integratie
Monte Carlo
Numerieke integratie
Monte Carlo

· Verklaring van het probleem, optie.

· Tekst van een parallelprogramma in C-taal volgens de opdracht.

· Resultaten van het uitvoeren van het programma op één knooppunt, uitvoeringstijd ti, berekeningsresultaat, fout.

· Resultaten van het uitvoeren van het programma op de server, uitvoeringstijd, berekeningsresultaten, fout.

· Beschrijf het parallelle algoritme, de informatiestromen tijdens de uitvoering van het programma en het laden van het cachegeheugen van het knooppunt. Bereken de Amdahl-coëfficiënt - K j op basis van de resultaten van het programma.

· Rekening houdend met de resultaten van het werk van een groep leerlingen, maak een histogram van de afhankelijkheid van K j, t i van het aantal processors dat aan de berekeningen deelneemt.

Annotatie: De lezing is gewijd aan de beschouwing van MPI-technologie als een parallelle programmeerstandaard voor gedistribueerde geheugensystemen. De belangrijkste vormen van gegevensoverdracht worden overwogen. Begrippen als procesgroepen en communicatoren worden geïntroduceerd. Omvat basisgegevenstypen, point-to-point-bewerkingen, collectieve bewerkingen, synchronisatiebewerkingen en tijdmetingen.

Doel van de lezing: De lezing is gericht op het bestuderen van de algemene methodologie voor het ontwikkelen van parallelle algoritmen.

Video-opname van de lezing - (volume - 134 MB).

5.1. MPI: basisconcepten en definities

Laten we een aantal concepten en definities bekijken die fundamenteel zijn voor de MPI-standaard.

5.1.1. Het concept van een parallel programma

Onder parallel programma Binnen het raamwerk van MPI verstaan ​​we een reeks gelijktijdig uitgevoerde handelingen processen. Processen kunnen op verschillende processors worden uitgevoerd, maar er kunnen ook meerdere processen op dezelfde processor worden geplaatst (in dit geval worden ze uitgevoerd in de time-sharing-modus). In het extreme geval kan een enkele processor worden gebruikt om een ​​parallel programma uit te voeren - in de regel wordt deze methode gebruikt om in eerste instantie de juistheid van het parallelle programma te controleren.

Elk proces van een parallel programma komt voort uit een kopie van dezelfde programmacode ( SPMP-model). Deze programmacode, gepresenteerd in de vorm van een uitvoerbaar programma, moet op het moment dat het parallelle programma wordt gestart op alle gebruikte processors beschikbaar zijn. De broncode voor het uitvoerbare programma is ontwikkeld in de algoritmische talen C of Fortran met behulp van een of andere implementatie van de MPI-bibliotheek.

Het aantal processen en het aantal gebruikte processors worden bepaald op het moment dat het parallelle programma wordt gestart met behulp van de MPI-programma-uitvoeringsomgeving en kunnen tijdens de berekeningen niet veranderen (de MPI-2-standaard biedt de mogelijkheid om het aantal processen dynamisch te wijzigen). Alle programmaprocessen zijn opeenvolgend genummerd van 0 tot p-1, Waar P is het totale aantal processen. Het procesnummer wordt gebeld rang proces.

5.1.2. Gegevensoverdrachtbewerkingen

MPI is gebaseerd op het doorgeven van berichten. Er zijn verschillende functies die worden aangeboden als onderdeel van MPI verdubbelt (punt-tot-punt) bewerkingen tussen twee processen en collectief (collectief) communicatieacties voor de gelijktijdige interactie van meerdere processen.

Om gepaarde handelingen uit te voeren kunnen verschillende transmissiemodi worden gebruikt, waaronder synchroon, blokkeren, enz. - waarbij alle mogelijke transmissiemodi zal worden uitgevoerd in paragraaf 5.3.

Zoals eerder opgemerkt, voorziet de MPI-standaard in de noodzaak om de meeste basisoperaties voor collectieve gegevensoverdracht te implementeren - zie paragrafen 5.2 en 5.4.

5.1.3. Concept van communicatoren

Processen van een parallel programma worden gecombineerd tot groepen. Onder communicator MPI verwijst naar een speciaal gemaakt serviceobject dat een groep processen en een aantal aanvullende parameters combineert ( context) gebruikt bij het uitvoeren van gegevensoverdrachtbewerkingen.

Meestal worden gepaarde gegevensoverdrachtbewerkingen uitgevoerd voor processen die tot dezelfde communicator behoren. Collectieve operaties worden gelijktijdig toegepast op alle communicatorprocessen. Als gevolg hiervan is het opgeven van de te gebruiken communicator verplicht voor gegevensoverdrachtbewerkingen in MPI.

Tijdens berekeningen kunnen nieuwe procesgroepen en communicators worden aangemaakt en bestaande groepen processen en communicators worden verwijderd. Hetzelfde proces kan tot verschillende groepen en communicatoren behoren. Alle processen die aanwezig zijn in het parallelle programma zijn opgenomen in de communicator die standaard is gemaakt met de identificatie MPI_COMM_WORLD.

Als het nodig is om gegevens over te dragen tussen processen van verschillende groepen, is het noodzakelijk om een ​​globale communicator te creëren ( intercommunicator).

Een gedetailleerde bespreking van de mogelijkheden van MPI voor het werken met groepen en communicatoren zal worden uitgevoerd in paragraaf 5.6.

5.1.4. Gegevenstypen

Wanneer u bewerkingen voor het doorgeven van berichten uitvoert, moet u de gegevens opgeven die moeten worden verzonden of ontvangen in MPI-functies. type verzonden gegevens. MPI bevat een grote set basistypen data die grotendeels samenvalt met datatypen in de algoritmische talen C en Fortran. Bovendien heeft MPI het vermogen om nieuwe te creëren afgeleide typen gegevens voor een nauwkeurigere en beknoptere beschrijving van de inhoud van doorgestuurde berichten.

Een gedetailleerde bespreking van de mogelijkheden van MPI voor het werken met afgeleide gegevenstypen zal worden uitgevoerd in paragraaf 5.5.

5.1.5. Virtuele topologieën

Zoals eerder opgemerkt kunnen gepaarde gegevensoverdrachtbewerkingen worden uitgevoerd tussen alle processen van dezelfde communicator, en nemen alle processen van de communicator deel aan een collectieve bewerking. In dit opzicht heeft de logische topologie van communicatielijnen tussen processen de structuur van een volledige grafiek (ongeacht de aanwezigheid van echte fysieke communicatiekanalen tussen processors).

Tegelijkertijd (en dit werd al opgemerkt in Hoofdstuk 3) is het voor de presentatie en daaropvolgende analyse van een aantal parallelle algoritmen raadzaam om te beschikken over een logische representatie van het bestaande communicatienetwerk in de vorm van bepaalde topologieën.

MPI heeft de mogelijkheid om meerdere processen in het formulier weer te geven roosters willekeurige dimensie (zie paragraaf 5.7). In dit geval kunnen de grensprocessen van de roosters aangrenzend worden verklaard en daardoor, op basis van de roosters, structuren van het type torus.

Daarnaast beschikt MPI over tools voor het genereren van logische (virtuele) topologieën van elk gewenst type. Een gedetailleerde bespreking van de mogelijkheden van MPI voor het werken met topologieën zal worden uitgevoerd in paragraaf 5.7.

En tot slot nog een laatste reeks opmerkingen voordat we naar MPI gaan kijken:

  • Beschrijvingen van functies en alle aangeboden voorbeelden van programma's zullen worden gepresenteerd in de algoritmische taal C; kenmerken van het gebruik van MPI voor de algoritmische taal Fortran zullen worden gegeven in paragraaf 5.8.1,
  • Een korte beschrijving van de beschikbare implementaties van MPI-bibliotheken en een algemene beschrijving van de uitvoeringsomgeving van MPI-programma's zullen worden besproken in paragraaf 5.8.2.
  • De belangrijkste presentatie van MPI-mogelijkheden zal gericht zijn op de versie 1.2-standaard ( MPI-1); aanvullende eigenschappen van de standaard versie 2.0 worden gepresenteerd in clausule 5.8.3.

Wanneer we MPI gaan bestuderen, kan worden opgemerkt dat MPI enerzijds behoorlijk complex is: de MPI-standaard voorziet in de aanwezigheid van meer dan 125 functies. Aan de andere kant is de structuur van MPI zorgvuldig doordacht: de ontwikkeling van parallelle programma's kan beginnen nadat slechts 6 MPI-functies zijn overwogen. Alle extra functies van MPI kunnen worden beheerst naarmate de complexiteit van de ontwikkelde algoritmen en programma's toeneemt. Het is in deze stijl – van eenvoudig tot complex – dat al het educatieve materiaal over MPI verder zal worden gepresenteerd.

5.2. Inleiding tot parallelle programmaontwikkeling met behulp van MPI

5.2.1. MPI-basisprincipes

Laten we de minimaal vereiste set MPI-functies presenteren, voldoende voor de ontwikkeling van tamelijk eenvoudige parallelle programma's.

5.2.1.1 Initialisatie en beëindiging van MPI-programma's

Eerste functie gebeld MPI moet een functie zijn:

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

om de uitvoeringsomgeving van het MPI-programma te initialiseren. De functieparameters zijn het aantal argumenten op de opdrachtregel en de tekst van de opdrachtregel zelf.

Laatst gebelde functie MPI moet een functie zijn:

int MPI_Finalize(ongeldig);

Als gevolg hiervan kan worden opgemerkt dat de structuur van een parallel programma dat is ontwikkeld met behulp van MPI de volgende vorm zou moeten hebben:

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

Opgemerkt moet worden:

  1. Bestand mpi.h bevat definities van benoemde constanten, functieprototypes en gegevenstypen van de MPI-bibliotheek,
  2. Functies MPI_Init En MPI_Finaliseren zijn verplicht en moeten (en slechts één keer) worden uitgevoerd door elk proces van het parallelle programma,
  3. Vóór de oproep MPI_Init functie kan worden gebruikt MPI_geïnitialiseerd om te bepalen of er eerder een oproep is gedaan MPI_Init.

De hierboven besproken voorbeelden van functies geven een idee van de syntaxis voor het benoemen van functies in MPI. De functienaam wordt voorafgegaan door het MPI-voorvoegsel, gevolgd door een of meer woorden van de naam, het eerste woord van de functienaam begint met een hoofdletter en de woorden worden gescheiden door een onderstrepingsteken. De namen van MPI-functies verklaren in de regel het doel van de acties die door de functie worden uitgevoerd.

Opgemerkt moet worden:

  • Communicator MPI_COMM_WERELD, zoals eerder opgemerkt, wordt standaard gemaakt en vertegenwoordigt alle processen van het parallelle programma dat wordt uitgevoerd,
  • Rang verkregen met behulp van de functie MPI_Comm_rang, is de rangorde van het proces dat deze functie heeft aangeroepen, d.w.z. variabel ProcRank zal in verschillende processen verschillende waarden aannemen.

Toevallig kwam ik in aanraking met de studie van parallel computing en in het bijzonder MPI. Misschien is deze richting vandaag de dag veelbelovend, dus ik zou de hubbrowsers de basisprincipes van dit proces willen laten zien.

Basisprincipes en voorbeeld
De berekening van exponentieel (e) zal als voorbeeld worden gebruikt. Een van de opties om het te vinden is de Taylor-serie:
e^x=∑((x^n)/n!), waarbij de sommatie plaatsvindt van n=0 tot oneindig.

Deze formule kan eenvoudig worden geparallelliseerd, aangezien het vereiste aantal de som is van individuele termen en dankzij dit kan elke individuele processor beginnen met het berekenen van de individuele termen.

Het aantal termen dat in elke individuele processor zal worden berekend, hangt zowel af van de lengte van het interval n als van het beschikbare aantal processors k dat kan deelnemen aan het berekeningsproces. Als de lengte van het interval bijvoorbeeld n=4 is en er zijn vijf processors (k=5) bij de berekeningen betrokken, dan krijgen de eerste tot en met de vierde processor elk één term en wordt de vijfde niet gebruikt. Als n=10 en k=5, krijgt elke processor twee termen voor berekening.

Aanvankelijk verzendt de eerste processor, met behulp van de MPI_Bcast-broadcastfunctie, naar de anderen de waarde van de door de gebruiker gespecificeerde variabele n. Over het algemeen heeft de functie MPI_Bcast het volgende formaat:
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm), waarbij buffer het adres is van de buffer met het element, count het aantal elementen is, datatype het overeenkomstige gegevenstype in MPI is, root is de rang van de hoofdprocessor die het doorsturen afhandelt, en comm is de naam van de communicator.
In mijn geval zal de rol van de hoofdprocessor, zoals reeds vermeld, de eerste processor met rang 0 zijn.

Nadat het getal n met succes is verzonden, begint elke processor met het berekenen van de voorwaarden ervan. Om dit te doen, wordt bij elke stap van de cyclus een getal dat gelijk is aan het aantal processors dat aan de berekeningen deelneemt, toegevoegd aan het getal i, dat aanvankelijk gelijk is aan de rangorde van de processor. Als het getal i in de volgende stappen het door de gebruiker opgegeven getal n overschrijdt, stopt de lusuitvoering voor die processor.

Tijdens de uitvoering van de cyclus worden de termen toegevoegd aan een afzonderlijke variabele en na voltooiing ervan wordt het resulterende bedrag naar de hoofdprocessor gestuurd. Om dit te doen, wordt de reductiefunctie MPI_Reduce gebruikt. Over het algemeen ziet het er zo uit:
int MPI_Reduce(void *buf, void *result, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)

Het voegt de invoerbufferelementen van elk proces in de groep samen met behulp van de op-bewerking en retourneert de gecombineerde waarde naar de uitvoerbuffer van procesnummer root. Het resultaat van een dergelijke bewerking zal één enkele waarde zijn, vandaar dat de castingfunctie zijn naam heeft gekregen.

Nadat het programma op alle processors is uitgevoerd, ontvangt de eerste processor de totale som van termen, wat de exponentwaarde is die we nodig hebben.

Opgemerkt moet worden dat bij zowel parallelle als sequentiële methoden voor het berekenen van de exponent een recursieve functie wordt gebruikt om de faculteit te vinden. Toen ik een beslissing nam over hoe ik de uit te voeren taak moest parallelliseren, overwoog ik de optie om de faculteit ook op verschillende processors te vinden, maar uiteindelijk werd deze optie door mij als irrationeel beschouwd.

De primaire taak is nog steeds het vinden van de waarde van de exponent, en als processors elke faculteit van elke term afzonderlijk gaan berekenen, kan dit tot precies het tegenovergestelde effect leiden, namelijk een aanzienlijk verlies aan prestaties en rekensnelheid.
Dit wordt verklaard door het feit dat er in dit geval een zeer grote belasting zal zijn voor de communicatieomgeving, die vaak al een zwakke schakel is in parallelle computersystemen. Als de faculteit op elke processor privé wordt berekend, zal de belasting van de communicatielijnen minimaal zijn. Dit geval kan een goed voorbeeld worden genoemd van het feit dat de taak van parallellisatie soms ook zijn grenzen moet hebben.

Code-uitvoeringsalgoritme
1. De waarde van het getal n wordt overgebracht van de visuele shell naar het programma, dat vervolgens naar alle processors wordt verzonden met behulp van de uitzendfunctie.
2. Wanneer de eerste hoofdprocessor wordt geïnitialiseerd, start er een timer.
3. Elke processor voert een lus uit, waarbij de incrementwaarde het aantal processors in het systeem is. Bij elke iteratie van de lus wordt een term berekend en de som van dergelijke termen wordt opgeslagen in de drobSum-variabele.
4. Nadat de lus is voltooid, voegt elke processor zijn drobSum-waarde toe aan de resultaatvariabele met behulp van de MPI_Reduce-reductiefunctie.
5. Nadat de berekeningen op alle processors zijn voltooid, stopt de eerste hoofdprocessor de timer en verzendt de resulterende waarde van de resultaatvariabele naar de uitvoerstroom.
6. De door onze timer gemeten tijdswaarde in milliseconden wordt ook naar de uitvoerstroom verzonden.
Codelijst
Het programma is geschreven in C++, we gaan ervan uit dat de argumenten voor uitvoering vanuit de externe shell worden doorgegeven. De code ziet er als volgt uit:
#erbij betrekken "mpi.h"
#erbij betrekken
#erbij betrekken
namespace std; gebruiken;

dubbel Feit(int n)
{
als (n==0)
retour 1;
anders
retourneer n*Feit(n-1);
}

int hoofd(int argc, char *argv)
{
SetConsoleOutputCP(1251);
int n;
int myid;
int numprocs;
int ik;
int rc;
lange dubbele drob,drobSum=0,Resultaat, som;
dubbele starttijd = 0,0;
dubbele eindtijd;

N = atoi(argv);

als (rc= MPI_Init(&argc, &argv))
{
uit<< "Opstartfout, uitvoering gestopt" << endl;
MPI_Abort(MPI_COMM_WORLD, rc);
}

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

als (myid == 0)
{

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

voor (i = myid; i<= n; i += numprocs)
{
drob = 1/Feit(i);
drobSom += drob;
}

MPI_Reduce(&drobSum, &Resultaat, 1, MPI_LONG_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
cout.precisie(20);
als (myid == 0)
{
uit<< Result << endl;
endwtime = MPI_Wtime();
uit<< (endwtime-startwtime)*1000 << endl;
}

MPI_Finalize();
retour 0;
}


* Deze broncode is gemarkeerd met Source Code Highlighter.
Conclusie
We ontvingen dus een eenvoudig programma voor het berekenen van de exponent met meerdere processors tegelijk. Waarschijnlijk is het knelpunt het opslaan van het resultaat zelf, omdat met een toename van het aantal cijfers het opslaan van een waarde met behulp van standaardtypen niet triviaal zal zijn en deze plek nadere uitwerking vereist. Misschien is een redelijk rationele oplossing om het resultaat naar een bestand te schrijven, hoewel het gezien de puur educatieve functie van dit voorbeeld niet nodig is om hier veel aandacht aan te besteden.

Het uitvoeren van een MPI-toepassing op een computercluster is alleen mogelijk via het batchverwerkingssysteem. Om het starten en in de wachtrij plaatsen van een parallel programma te vereenvoudigen, is er een speciaal mpirun-script beschikbaar. Mpirun -np 20 ./first.exe zal bijvoorbeeld het parallelle programma first.exe uitvoeren op 20 processors, d.w.z. op 5 knooppunten. (Elk knooppunt heeft 2 dual-coreprocessors). Het is vermeldenswaard dat om een ​​uitvoerbare module te starten die zich in de huidige map ($pwd) bevindt, u expliciet het pad “./” moet opgeven. Een aantal MPI-1-implementaties bieden een startopdracht voor MPI-programma's, die de vorm mpirun heeft.<аргументы mpirun><программа><аргументы программы>

Het scheiden van de opdracht voor het starten van het programma en het programma zelf biedt flexibiliteit, vooral voor netwerk- en heterogene implementaties. Het hebben van een standaard startmechanisme breidt de overdraagbaarheid van MPI-programma's ook een stap verder uit naar opdrachtregels en de scripts die deze manipuleren. Een script voor een reeks validatieprogramma's die honderden programma's uitvoeren, kan bijvoorbeeld een overdraagbaar script zijn als het is geschreven met behulp van een dergelijk standaard startmechanisme. Om het ``standaard'' commando niet te verwarren met het bestaande commando in de praktijk, dat niet standaard is en niet overdraagbaar is tussen implementaties, heeft MPI mpiexec gedefinieerd in plaats van mpirun.

Hoewel een gestandaardiseerd opstartmechanisme de bruikbaarheid van MPI verbetert, is het scala aan omgevingen zo divers (er is bijvoorbeeld misschien niet eens een opdrachtregelinterface) dat MPI een dergelijk mechanisme niet kan verplichten. In plaats daarvan definieert MPI de opdracht mpiexec run en beveelt het aan, maar vereist dit niet, als advies aan ontwikkelaars. Als een implementatie echter een opdracht levert met de naam mpiexec, moet deze de hieronder beschreven vorm aannemen: mpiexec -n <программа>

er zal minstens één manier zijn om te rennen<программу>met initiële MPI_COMM_WORLD waarvan de groep bevat processen. Andere argumenten voor mpiexec kunnen afhankelijk zijn van de implementatie.

Voorbeeld 4.1 16 exemplaren van myprog uitvoeren op de huidige of standaardmachine:

mpiexec -n 16 mijnprog

3. Schrijf een programma voor de parallelle berekening van de bepaalde integraal van de functie 2*(x+2*x*x/1200.0) in het interval .

Methode voor de linker rechthoek

dubbele f(dubbele x)

(retourneert 2*(x+2*x*x/1200);) // iskomyi-integraal

int hoofd(int argc,char **argv)

MPI_Status-status;

MPI_Init(&argc,&argv);

MPI_Comm_rang(MPI_COMM_WERELD,&rang);

MPI_Comm_grootte(MPI_COMM_WERELD,&grootte);

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

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

if (rang!=grootte-1) // schitaut vse processy, krome poslednego

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

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

( voor (i=0; ik

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

MPI_Finalize();)

Surak

1. Gedeelde en gedistribueerde geheugenarchitecturen.

Gedistribueerd gedeeld geheugen (DSM - Distributed Shared Memory)

Traditioneel is gedistribueerd computergebruik gebaseerd op een message-passing-model, waarbij gegevens in de vorm van berichten van processor naar processor worden doorgegeven. Remote procedure calls zijn eigenlijk hetzelfde model (of heel dichtbij). DSM is een virtuele adresruimte die wordt gedeeld door alle knooppunten (processors) van een gedistribueerd systeem. Programma's hebben in DSM op vrijwel dezelfde manier toegang tot gegevens als tot gegevens in het virtuele geheugen van traditionele computers. In systemen met DSM verplaatsen gegevens zich tussen lokale herinneringen van verschillende computers op dezelfde manier als tussen RAM en extern geheugen van één computer. De gedistribueerde gedeelde geheugenconfiguratie is een variant van gedistribueerd geheugen. Hier gebruiken alle knooppunten, bestaande uit een of meer processors die via een SMP-schema zijn verbonden, een gemeenschappelijke adresruimte. Het verschil tussen deze configuratie en een machine met gedistribueerd geheugen is dat hier elke processor toegang heeft tot elk deel van het geheugen. De toegangstijd voor verschillende geheugensecties varieert echter voor elke processor, afhankelijk van waar de sectie zich fysiek in het cluster bevindt. Om deze reden worden dergelijke configuraties ook wel machines met niet-uniforme geheugentoegang (NUMA) genoemd.

Verschillen tussen MPI en PVM.

Het PVM-systeem (Parallel Virtual Machine) is gemaakt om verschillende netwerkwerkstations te combineren tot één virtuele parallelle computermachine. Het systeem is een add-on voor het UNIX-besturingssysteem en wordt gebruikt op verschillende hardwareplatforms, inclusief massaal parallelle systemen. De meest voorkomende parallelle programmeersystemen zijn tegenwoordig gebaseerd op MPI (Message Parsing Interface). Het idee van MPI is in eerste instantie eenvoudig en voor de hand liggend. Het omvat het representeren van een parallel programma als een reeks parallelle uitvoeringsprocessen die met elkaar communiceren tijdens de uitvoering van gegevensoverdracht met behulp van communicatieprocedures. Zij vormen de MPI-bibliotheek. Een juiste implementatie van MPI ter ondersteuning van communicatie tussen processors is echter behoorlijk moeilijk gebleken. Deze complexiteit houdt verband met de noodzaak om hoge programmaprestaties te bereiken, de noodzaak om talrijke multicomputerbronnen te gebruiken en, als gevolg daarvan, een grote verscheidenheid in de implementatie van communicatieprocedures, afhankelijk van de gegevensverwerkingsmodus.

Parallellisatie in C-taal
Voorbeeld 3b. Parallellisatie in Fortran
Voorbeeld 4a. Bepalen van de kenmerken van de systeemtimer in C-taal
Voorbeeld 4b. Kenmerken van systeemtimers definiëren in Fortran

1.4. Berichten verzenden en ontvangen tussen afzonderlijke processen

1.4.1. Point-to-point-bewerkingen

1.4.2. Berichten verzenden en ontvangen met blokkering

Voorbeeld 5a. Uitwisseling van berichten tussen twee processen in C-taal
Voorbeeld 5b. Uitwisseling van berichten tussen twee processen in Fortran
Voorbeeld 6a. Berichtenuitwisseling tussen even en oneven processen in C
Voorbeeld 6b. Berichtenuitwisseling tussen even en oneven processen in Fortran
Voorbeeld 7a. Doorsturen naar een niet-bestaand proces in C
Voorbeeld 7b. Doorsturen naar een niet-bestaand proces in Fortran
Voorbeeld 8a. Gebufferde gegevensverzending in C-taal
Voorbeeld 8b. Gebufferde gegevensverzending in Fortran-taal
Voorbeeld 9a. Informatie verkrijgen over berichtkenmerken in C-taal
Voorbeeld 9b. Informatie verkrijgen over berichtkenmerken in Fortran
Voorbeeld 10a. Definitie van latentie en doorvoer in C-taal
Voorbeeld 10b. Latentie en doorvoer definiëren in Fortran

1.4.3. Berichten verzenden en ontvangen zonder te blokkeren

Voorbeeld 11a. Uitwisseling via een ringtopologie met behulp van niet-blokkerende bewerkingen in C
Voorbeeld 11b. Uitwisseling via een ringtopologie met behulp van niet-blokkerende bewerkingen in Fortran
Voorbeeld 12a. Communicatieschema "meester - werkers" in C-taal
Voorbeeld 12b. Communicatiediagram "meester - arbeiders" in Fortran-taal
Voorbeeld 13a. Matrixtranspositie in C-taal
Voorbeeld 13b. Een matrix omzetten in Fortran

1.4.4. Interactieverzoeken die in behandeling zijn

Voorbeeld 14a. Schema van een iteratieve methode met uitwisseling langs een ringtopologie met behulp van uitgestelde zoekopdrachten in C-taal
Voorbeeld 14b. Schema van een iteratieve methode met uitwisseling via een ringtopologie met behulp van uitgestelde zoekopdrachten in Fortran

1.4.5. Impasse situaties

Voorbeeld 15a. Uitwisseling via een ringtopologie met behulp van de MPI_Sendrecv-procedure in C-taal
Voorbeeld 15b. Uitwisseling via een ringtopologie met behulp van de MPI_SENDRECV-procedure in Fortran

1.5. Collectieve procesinteracties

1.5.1. Algemene bepalingen

1.5.2. Barrière

Voorbeeld 16a. Modellering van barrièresynchronisatie in C-taal
Voorbeeld 16b. Modellering van barrièresynchronisatie in Fortran

1.5.3. Collectieve gegevensoverdrachtsoperaties

1.5.4. Globale operaties

Voorbeeld 17a. Modelleren van globale sommatie met behulp van een verdubbelingsschema en de collectieve bewerking MPI_Reduce in C-taal
Voorbeeld 17b. Modellering van globale sommatie met behulp van een verdubbelingsschema en de collectieve bewerking MPI_Reduce in Fortran

1.5.5. Aangepaste wereldwijde operaties

Voorbeeld 18a. Aangepaste globale functie in C-taal
Voorbeeld 18b. Aangepaste globale functie in Fortran

1.6. Groepen en communicatoren

1.6.1. Algemene bepalingen

1.6.2. Bewerkingen met procesgroepen

Voorbeeld 19a. Werken met groepen in C-taal
Voorbeeld 19b. Werken met groepen in Fortran

1.6.3. Operaties met communicatoren

Voorbeeld 20a. Het afbreken van een communicator in C
Voorbeeld 20b. Een communicator partitioneren in Fortran
Voorbeeld 21a. Hernummering van processen in C-taal
Voorbeeld 21b. Hernummering van processen in Fortran

1.6.4. Intercommunicatoren

Voorbeeld 22a. Master-worker-schema met behulp van een intercommunicator in C-taal
Voorbeeld 22b. Master-worker-circuit met behulp van een intercommunicator in Fortran

1.6.5. Kenmerken

1.7. Virtuele topologieën

1.7.1. Algemene bepalingen

1.7.2. Cartesiaanse topologie

1.7.3. Grafiektopologie

Voorbeeld 23a. Master-worker-diagram met behulp van grafiektopologie in C-taal
Voorbeeld 23b. Master-worker-schema met behulp van grafiektopologie in Fortran

1.8. Verschillende soorten gegevens verzenden

1.8.1. Algemene bepalingen

1.8.2. Afgeleide gegevenstypen

Voorbeeld 24a. Herschikken van matrixkolommen in omgekeerde volgorde in C-taal
Voorbeeld 24b. Herschikken van matrixkolommen in omgekeerde volgorde in Fortran

1.8.3. Gegevensverpakking

Voorbeeld 25a. Verpakte gegevens verzenden in C-taal
Voorbeeld 25b. Verpakte gegevens verzenden in Fortran

1.9. info-object

1.9.1. Algemene bepalingen

1.9.2. Werken met het info-object

1.10. Dynamische procescontrole

1.10.1. Algemene bepalingen

1.10.2.Creatie van processen

meester.c
slaaf.c
Voorbeeld 26a. Master-worker-schema met behulp van process spawning in C-taal
meester.f
slaaf.f
Voorbeeld 26b. Master-worker-schema met behulp van process spawning in Fortran

1.10.3. Client-server-communicatie

server.c
klant.c
Voorbeeld 27a. Uitwisseling van gegevens tussen server en client met behulp van een openbare naam in C-taal
server.f
klant.f
Voorbeeld 27b. Uitwisseling van gegevens tussen server en client met behulp van een openbare naam in de Fortran-taal

1.10.4. Een proceskoppeling verwijderen

1.10.5. Socket-communicatie

1.11. Communicatie in één richting

1.11.1. Algemene bepalingen

1.11.2. Werken met een raam

1.11.3. Data overdracht

1.11.4. Synchronisatie

Voorbeeld 28a
Voorbeeld 28b
Voorbeeld 29a. Uitwisseling via een ringtopologie met behulp van eenrichtingscommunicatie in C
Voorbeeld 29b. Uitwisseling via een ringtopologie met behulp van eenrichtingscommunicatie in Fortran
Voorbeeld 30a. Uitwisseling via een ringtopologie met behulp van eenrichtingscommunicatie in C
Voorbeeld 30b. Uitwisseling via een ringtopologie met behulp van eenrichtingscommunicatie in Fortran

1.12. Externe interfaces

1.12.1. Algemene vragen

1.12.2. Informatie uit status

1.12.3. Draden

1.13. Parallelle I/O

1.13.1. Definities

1.13.2. Werken met bestanden

1.13.3. Toegang tot data

Voorbeeld 31a. Gebufferd lezen uit een bestand in C-taal
Voorbeeld 31b. Gebufferd lezen uit een bestand in Fortran
Voorbeeld 32a. Collectief lezen uit een bestand in C-taal
Voorbeeld 32b. Collectief lezen uit een bestand in Fortran

1.14. Foutverwerking

1.14.1. Algemene bepalingen

1.14.2. Foutafhandelaars geassocieerd met communicators

1.14.3. Venstergerelateerde foutafhandelaars

1.14.4. Bestandsgerelateerde foutafhandelaars

1.14.5. Aanvullende procedures

1.14.6. Foutcodes en klassen

1.14.7. Foutafhandelaars bellen

Voorbeeld 33a. Foutafhandeling in C-taal
Voorbeeld 33b. Foutafhandeling in Fortran

Hoofdstuk 2 OpenMP parallelle programmeertechnologie

2.1. Invoering

2.2. Basisconcepten

2.2.1. Het samenstellen van een programma

Voorbeeld 34a. Voorwaardelijke compilatie in C
Voorbeeld 34b
Voorbeeld 34c. Voorwaardelijke compilatie in Fortran

2.2.2. Parallel programmamodel

2.2.3. Richtlijnen en procedures

2.2.4. Uitvoering van het programma

2.2.5. Tijdstip

Voorbeeld 35a. Werken met systeemtimers in C
Voorbeeld 35b. Werken met systeemtimers in Fortran

2.3. Parallelle en seriële gebieden

2.3.1. parallelle richtlijn

Voorbeeld 36a. Parallelle regio in C-taal
Voorbeeld 36b. Parallelle regio in Fortran
Voorbeeld 37a. De reductieoptie in C-taal
Voorbeeld 37b. De reductieoptie in Fortran

2.3.2. Stenonotatie

2.3.3. Omgevingsvariabelen en helperprocedures

Voorbeeld 38a. Procedure omp_set_num_threads en optie num_threads in C-taal
Voorbeeld 38b. Procedure omp_set_num_threads en optie num_threads in Fortran-taal
Voorbeeld 39a. Procedures omp_set_dynamic en omp_get_dynamic in C-taal
Voorbeeld 39b. Procedures omp_set_dynamic en omp_get_dynamic in Fortran
Voorbeeld 40a. Geneste parallelle regio's in C
Voorbeeld 40b. Geneste parallelle regio's in Fortran
Voorbeeld 41a. Omp_in_parallel-functie in C-taal
Voorbeeld 41b. Functie omp_in_parallel in Fortran-taal

2.3.4. enkele richtlijn

Voorbeeld 42a. Enkele richtlijn en nowait-optie in C-taal
Voorbeeld 42b. Eén richtlijn en nowait-optie in Fortran
Voorbeeld 43a. Copyprivate-optie in C-taal
Voorbeeld 43b. copyprivate-optie in Fortran

2.3.5. hoofdrichtlijn

Voorbeeld 44a. Masterrichtlijn in C-taal
Voorbeeld 44b. Hoofdrichtlijn in Fortran

2.4. Gegevensmodel

Voorbeeld 45a. Privéoptie in C-taal
Voorbeeld 45b. De privéoptie in Fortran
Voorbeeld 46a. Gedeelde optie in C-taal
Voorbeeld 46b. De gedeelde optie in Fortran
Voorbeeld 47a. eerste privéoptie in C-taal
Voorbeeld 47b. eerste privéoptie in Fortran
Voorbeeld 48a. threadprivate-richtlijn in C-taal
Voorbeeld 48b. threadprivate-richtlijn in Fortran
Voorbeeld 49a. Kopieeroptie in C-taal
Voorbeeld 49b. kopieeroptie in Fortran

2.5. Werkverdeling

2.5.1. Parallellisatie op laag niveau

Voorbeeld 50a. Procedures omp_get_num_threads en omp_get_thread_num in C-taal
Voorbeeld 50b. Procedures omp_get_num_threads en omp_get_thread_num in Fortran

2.5.2. Parallelle lussen

Voorbeeld 51a. voor richtlijn in C-taal
Voorbeeld 51b. De do-richtlijn in Fortran
Voorbeeld 52a. Planningsoptie in C-taal
Voorbeeld 52b. planningsoptie in Fortran
Voorbeeld 53a. Planningsoptie in C-taal