Parallel Programming Technologies (MPI) tutvustus. Toimingud protsessirühmadega. direktiivid ja menetlused

See märkus näitab, kuidas MPI-d installida ja sellega ühendada Visual Studio ja seejärel kasutada koos antud parameetrid(arvutussõlmede arv). See artikkel kasutab Visual Studio 2015, kuna... See on see, millega minu õpilastel probleeme oli (selle märkuse kirjutasid õpilased õpilastele), kuid juhised töötavad tõenäoliselt ka teiste versioonide puhul.

Samm 1:
Peate installima HPC Pack 2008 SDK SP2 (teie puhul võib olla juba mõni muu versioon), mis on saadaval Microsofti ametlikul veebisaidil. Paki ja süsteemi bitimaht peab ühtima.

2. samm:
Peate konfigureerima teed; selleks minge vahekaardile Silumine - Atribuudid:

"C:\Program Files\Microsoft HPC Pack 2008 SDK\Include"

Väljal Library Directories:

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

Raamatukogudega põllul, kui maksab 32 bitine versioon, amd64 asemel peate registreerima i386.

Msmpi.lib

:

3. samm:

Käivitamise konfigureerimiseks peate minema vahekaardile Silumine ja määrama väljale Käsk:

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

Määrake väljale Käsuargumendid näiteks

N 4 $ (sihttee)

Number 4 näitab protsesside arvu.

Programmi käivitamiseks peate raamatukogu ühendama

Projekti tee ei tohi sisaldada kirillitsat. Kui ilmnevad vead, saate kasutada Microsoft MPI-d, mis on saadaval Microsofti veebisaidil.

Selleks sisestage pärast installimist tee vahekaardi Silumine väljale Käsk:

"C:\Program Files\Microsoft MPI\Bin\mpiexec.exe"

Samuti ärge unustage enne programmi käivitamist näidata selle bitisügavust:

Näide programmi käivitamisest MPI-ga:

#kaasa #kaasa kasutades nimeruumi std; int main(int argc, char **argv) ( int auaste, suurus; 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; }

Programmi käitamine kahes sõlmes:

Märkus: Loeng on pühendatud MPI-tehnoloogia kui hajusmälusüsteemide pkäsitlemisele. Vaadeldakse peamisi andmeedastusviise. Tutvustatakse selliseid mõisteid nagu protsessirühmad ja kommunikaatorid. Hõlmab põhiandmetüüpe, punktist punkti toiminguid, kollektiivseid operatsioone, sünkroonimistoiminguid ja ajamõõtmisi.

Loengu eesmärk: Loeng on suunatud paralleelalgoritmide väljatöötamise üldise metoodika uurimisele.

Loengu videosalvestus - (maht - 134 MB).

5.1. MPI: põhimõisted ja määratlused

Vaatleme mitmeid mõisteid ja määratlusi, mis on MPI standardi jaoks põhilised.

5.1.1. Paralleelprogrammi kontseptsioon

Under paralleelprogramm MPI raames mõistame samaaegselt täidetavate kogumit protsessid. Protsesse saab käivitada erinevatel protsessoritel, kuid samal protsessoril võib asuda ka mitu protsessi (antud juhul täidetakse neid ajajagamise režiimis). Äärmisel juhul saab paralleelprogrammi täitmiseks kasutada ühte protsessorit – reeglina kasutatakse seda meetodit paralleelprogrammi õigsuse esmaseks kontrollimiseks.

Iga paralleelse programmi protsess on loodud sama programmikoodi koopiast ( SPMP mudel). See käivitatava programmi kujul esitatud programmikood peab olema paralleelse programmi käivitamise ajal saadaval kõigis kasutatavates protsessorites. Käivitatava programmi lähtekood töötatakse välja algoritmilistes keeltes C või Fortran, kasutades ühte või teist MPI teegi teostust.

Protsesside arv ja kasutatavate protsessorite arv määratakse paralleelprogrammi käivitamise hetkel, kasutades MPI programmi täitmiskeskkonda ja ei saa arvutuste käigus muutuda (standard MPI-2 näeb ette protsesside arvu dünaamilise muutmise võimaluse). Kõik programmiprotsessid on järjestikku nummerdatud 0 kuni p-1, Kus lk on protsesside koguarv. Kutsutakse protsessi numbrit koht protsessi.

5.1.2. Andmeedastustoimingud

MPI põhineb sõnumi edastamise operatsioonidel. MPI osana pakutavate funktsioonide hulgas on erinevaid kahekohalised (punktist punktini) toimingud kahe protsessi vahel ja kollektiivne (kollektiivne) suhtlustoimingud mitme protsessi samaaegseks koostoimeks.

Paaritud toimingute sooritamiseks saab kasutada erinevaid edastusrežiime, sh sünkroonseid, blokeerimis- jne – võimalike võimaluste täielik kaalumine edastusrežiimid teostatakse punktis 5.3.

Nagu varem märgitud, näeb MPI standard ette vajaduse rakendada enamik põhilisi kollektiivseid andmeedastustoiminguid – vt alajaotisi 5.2 ja 5.4.

5.1.3. Suhtlejate kontseptsioon

Paralleelprogrammi protsessid on kombineeritud rühmad. Under suhtleja MPI viitab spetsiaalselt loodud teenindusobjektile, mis ühendab protsesside rühma ja mitmeid lisaparameetreid ( Sisu), mida kasutatakse andmeedastustoimingute tegemisel.

Tavaliselt tehakse paarisandmeedastusoperatsioone samasse kommunikaatorisse kuuluvate protsesside jaoks. Kollektiivseid operatsioone rakendatakse samaaegselt kõikidele suhtlemisprotsessidele. Seetõttu on MPI andmeedastustoimingute jaoks kasutatava kommunikaatori määramine kohustuslik.

Arvutuste käigus saab luua uusi protsessigruppe ja kommunikaatoreid ning kustutada olemasolevaid protsesside ja kommunikaatorite rühmi. Sama protsess võib kuuluda erinevatele rühmadele ja suhtlejatele. Kõik paralleelprogrammis olevad protsessid kaasatakse vaikimisi loodud kommunikaatorisse identifikaatoriga MPI_COMM_WORLD.

Kui on vaja andmeid erinevate rühmade protsesside vahel üle kanda, on vaja luua globaalne kommunikaator ( suhtleja).

Üksikasjalik arutelu MPI võimalustest rühmade ja suhtlejatega töötamiseks viiakse läbi alajaotises 5.6.

5.1.4. Andmetüübid

Sõnumi edastamise toimingute tegemisel tuleb MPI funktsioonides määrata saadetavad või vastuvõetavad andmed. tüüp saadetud andmed. MPI sisaldab suurt komplekti põhitüübid andmed, mis langevad suures osas kokku andmetüüpidega algoritmilistes keeltes C ja Fortran. Lisaks on MPI-l võimalus luua uut tuletatud tüübid andmed edastatud sõnumite sisu täpsemaks ja ülevaatlikumaks kirjeldamiseks.

Üksikasjalik arutelu MPI võimalustest tuletatud andmetüüpidega töötamiseks viiakse läbi alajaotises 5.5.

5.1.5. Virtuaalsed topoloogiad

Nagu varem märgitud, saab paarisandmeedastusoperatsioone sooritada sama kommunikaatori mis tahes protsesside vahel ja kõik kommunikaatori protsessid osalevad kollektiivses toimingus. Sellega seoses on protsessidevaheliste sideliinide loogiline topoloogia tervikliku graafiku struktuur (olenemata tegelike füüsiliste sidekanalite olemasolust protsessorite vahel).

Samal ajal (ja seda märgiti juba jaotises 3) on mitme paralleelse algoritmi esitamiseks ja järgnevaks analüüsiks soovitatav olemasoleva sidevõrgu loogiline esitus teatud topoloogiate kujul.

MPI-l on võimalus esitada vormil mitut protsessi restid suvaline mõõde (vt alajaotis 5.7). Sel juhul saab võre piirprotsessid kuulutada naaberteks ja seeläbi võre põhjal ka tüüpi struktuurid torus.

Lisaks on MPI-l tööriistad mis tahes nõutud tüüpi loogiliste (virtuaalsete) topoloogiate genereerimiseks. Üksikasjalik arutelu MPI võimalustest topoloogiatega töötamiseks viiakse läbi alajaotises 5.7.

Ja lõpuks üks viimane märkmete komplekt enne MPI vaatamist:

  • Funktsioonide kirjeldused ja kõik pakutavate programmide näited esitatakse algoritmilises keeles C; MPI kasutamise funktsioonid algoritmilise keele Fortran jaoks on toodud jaotises 5.8.1,
  • MPI teekide saadaolevate rakenduste lühikirjeldust ja MPI programmide täitmiskeskkonna üldist kirjeldust käsitletakse jaotises 5.8.2.
  • MPI võimaluste põhiesitlus keskendub versiooni 1.2 standardile ( MPI-1); versiooni 2.0 standardi lisaomadused esitatakse punktis 5.8.3.

MPI uurimist alustades võib märkida, et ühest küljest on MPI üsna keeruline - MPI standard näeb ette enam kui 125 funktsiooni olemasolu. Teisest küljest on MPI struktuur hoolikalt läbi mõeldud - paralleelprogrammide arendamine võib alata pärast vaid 6 MPI funktsiooni arvestamist. Kõiki MPI lisafunktsioone saab omandada, kui arendatud algoritmid ja programmid muutuvad keerukamaks. Just selles stiilis – lihtsast keerukani – esitatakse kogu MPI-alane õppematerjal edasi.

5.2. Sissejuhatus paralleelprogrammide arendusse MPI abil

5.2.1. MPI põhitõed

Esitame minimaalselt nõutava MPI funktsioonide komplekti, millest piisab üsna lihtsate paralleelprogrammide arendamiseks.

5.2.1.1 MPI programmide initsialiseerimine ja lõpetamine

Esimene funktsioon kutsutud MPI peaks olema funktsioon:

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

MPI programmi täitmiskeskkonna lähtestamiseks. Funktsiooni parameetrid on argumentide arv käsureal ja käsurea tekst ise.

Viimati kutsutud funktsioon MPI peab olema funktsioon:

int MPI_Finalize(void);

Selle tulemusena võib märkida, et MPI abil välja töötatud paralleelprogrammi struktuur peaks olema järgmisel kujul:

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

Tuleb märkida:

  1. Fail mpi.h sisaldab nimega konstantide, funktsioonide prototüüpide ja MPI teegi andmetüüpide määratlusi,
  2. Funktsioonid MPI_Init Ja MPI_Lõpeta on kohustuslikud ja neid peab käivitama (ja ainult üks kord) iga paralleelprogrammi protsess,
  3. Enne kõnet MPI_Init funktsiooni saab kasutada MPI_Initsialiseeritud et teha kindlaks, kas kõne on varem tehtud MPI_Init.

Eespool käsitletud funktsioonide näited annavad aimu funktsioonide nimetamise süntaksist MPI-s. Funktsiooni nimele eelneb MPI eesliide, millele järgneb üks või mitu nimesõna, funktsiooni nime esimene sõna algab suure tähega ja sõnad on eraldatud alakriipsuga. MPI funktsioonide nimetused selgitavad reeglina funktsiooni poolt sooritatavate toimingute eesmärki.

Tuleb märkida:

  • Suhtleja MPI_COMM_WORLD, nagu varem märgitud, luuakse vaikimisi ja esindab kõiki käivitatava paralleelprogrammi protsesse,
  • Funktsiooni abil saadud järjestus MPI_Comm_rank, on selle funktsiooni väljakutse teinud protsessi auaste, st. muutuv ProcRank võtab erinevates protsessides erinevaid väärtusi.

MPI funktsioonid

Tuletatud tüüp, operatsioonid, andmetüübid

Bsend Buffer_attach Get_count ANY_SOURCE Sendrecv_replace ANY_TAG Probe

Allgetherv Alltoall Alltoallv Vähenda Rduce_scatter Scan

Tuletatud tüüp koostatakse eelmääratletud MPI tüüpidest ja eelnevalt määratletud tuletatud tüüpidest, kasutades spetsiaalseid konstruktorifunktsioone

MPI_tüüp külgnev, MPI_tüüp_vektor, MPI_tüüp_hvektor, MPI_tüüp_indekseeritud, MPI_tüüp_hindexed, MPI_tüüp_struktuur.

Uus tuletatud tüüp registreeritakse funktsiooni MPI_Type_commit kutsumisega. Alles pärast registreerimist saab uut tuletatud tüüpi kasutada suhtlusrutiinides ja muude tüüpide koostamisel. Eelmääratletud MPI tüübid loetakse registreerituks.

Kui tuletatud tüüpi pole enam vaja, hävitatakse see funktsiooniga MPI_Type_free.

1) MPI_Init - lähtestamisfunktsioon. Selle funktsiooni täitmise tulemusel luuakse protsessirühm, kuhu paigutatakse kõik rakendusprotsessid, ja luuakse sideala, mida kirjeldab eelmääratletud kommunikaator MPI_COMM_WORLD.

MPI_Type_commit - tüübi registreerimine, MPI_Type_free - tüübi hävitamine

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

2) MPI_Finalize – funktsioon MPI programmide lõpetamiseks. Funktsioon sulgeb kõik MPI protsessid ja kõrvaldab kõik suhtluspiirkonnad.

int MPI_Finalize(void);

3) Funktsioon sidepiirkonna protsesside arvu määramiseks MPI_Comm_size . Funktsioon tagastab protsesside arvu kommunikaatori kommi suhtlusalal.

int MPI_Comm_size(MPI_Comm comm, int *suurus);

4) Protsessi numbri tuvastamise funktsioon MPI_Comm_rank . Funktsioon tagastab selle funktsiooni kutsunud protsessi numbri. Protsessi numbrid jäävad vahemikku 0..suurus-1.

int MPI_Comm_rank(MPI_Comm comm, int *rank);

5) Sõnumi funktsioon MPI_Saada. Funktsioon saadab identifikaatorimärgisega sõnumi andmetüübi loenduselemendid, et töödelda kommunikaatori kommi suhtlusalal asuvat sihtpunkti.

int MPI_Saada(void* buf, int count, MPI_Datatype andmetüüp, int dest, int silt, MPI_Comm comm);

6) Sõnumite vastuvõtu funktsioon MPI_Recv. Funktsioon võtab kommunikaatori kommi sidealal allikaprotsessist vastu identifikaatorimärgisega sõnumi andmetüübi loenduselemendid.

int MPI_Recv(void* buf, int count, MPI_Datatype andmetüüp, int allikas, int silt, MPI_Comm comm, MPI_Status *olek)

7) Ajastusfunktsioon (taimer) MPI_Wtime. Funktsioon tagastab astronoomilise aja sekundites, mis on möödunud mingist minevikuhetkest (võrdluspunkt).

double MPI_Wtime(tühine)

Funktsioonid sõnumite edastamiseks protsesside vahel jagunevad:

Prefiks S (sünkroonne)

tähendab sünkroonset andmeedastusrežiimi. Andmeedastustoiming lõpeb alles siis, kui andmete vastuvõtt lõpeb. Funktsioon ei ole kohalik.

Eesliide B (puhverdatud)

tähendab puhverdatud andmeedastusrežiimi. Saatmisprotsessi aadressiruumi luuakse spetsiaalse funktsiooni abil lõikelaud, mida kasutatakse vahetustoimingutes. Saatmistoiming lõpeb, kui andmed sisestatakse sellesse puhvrisse. Funktsioon on oma olemuselt kohalik.

Prefiks R (valmis)

kokkulepitud või ettevalmistatud andmeedastusviis. Andmeedastusoperatsioon algab alles siis, kui vastuvõttev protsessor on seadnud andmete vastuvõtmiseks valmisoleku märgi, algatades vastuvõtutoimingu. Funktsioon ei ole kohalik.

Eesliide I (kohe)

viitab mitteblokeerivatele toimingutele.

MPI_Status struktuur

Pärast sõnumi lugemist võivad mõned parameetrid olla teadmata, näiteks loetud üksuste arv, sõnumi ID ja saatja aadress. Seda teavet saab hankida olekuparameetri abil. Olekumuutujad peavad olema MPI programmis selgesõnaliselt deklareeritud. C-keeles on olek kolme väljaga MPI_Status struktuur MPI_SOURCE, MPI_TAG, MPI_ERROR.

8) Tegelikult vastuvõetud sõnumielementide arvu määramiseks peate kasutama spetsiaalset funktsiooni MPI_Get_count .

int MPI_Get_count (MPI_Status *status, MPI_Datatype andmetüüp, int *count);

9) Saate määrata vastuvõetud sõnumi parameetrid ilma seda lugemata funktsiooni MPI_Probe abil. int MPI_Probe (int allikas, int silt, MPI_Comm comm, MPI_Status *olek);

10) Olukordades, kus on vaja protsesside vahel andmeid vahetada, on kindlam kasutada kombineeritud operatsiooni MPI_Sendrecv. Selles toimingus asendatakse buf-massiivist saadetud andmed vastuvõetud andmetega.

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);

11) Funktsioon mitteblokeeriva toimingu MPI_Test lõpuleviimise kontrollimiseks.

int MPI_Test(MPI_Request *päring, int *lipp, MPI_Olek *olek);

See on kohalik mitteblokeeriv toiming. Kui päringuga seotud toiming on lõpule viidud, tagastatakse lipp = true ja olek sisaldab teavet lõpetatud toimingu kohta. Kui kontrollitav toiming pole lõpule viidud, tagastatakse lipp = false ja oleku väärtus on sel juhul määramata.

12) Funktsioon päringu tühistamiseks, ootamata mitteblokeeriva toimingu MPI_Request_free lõpetamist.

int MPI_taotlus_vaba(MPI_taotlus *päring);

Taotluse parameeter on seatud väärtusele MPI_REQUEST_NULL.

13) Andmeedastustoimingu tõhus teostamine ühest protsessist programmi kõikidesse protsessidesse (andmeedastus) on võimalik MPI funktsiooni abil:

int MPI_Bcast(kehtetu *buf, int count, MPI_andmetüüp, int juur, MPI_Comm)

Funktsioon MPI_Bcast edastab andmeid puhverpuhvrist, mis sisaldab loendustüüpi elemente, protsessi nummerdatud juurtest kõikidele sidekommunikatsiooniseadmes sisalduvatele protsessidele.

14) Kui teil on vaja kelleltki sõnumit saada saatmisprotsessil võib olla lähteparameetri jaoks määratud väärtus MPI_ANY_SOURCE

15) Kui on vaja saada sõnum mis tahes sildiga, saab märgendi parameetri väärtuse määrata MPI_ANY_TAG

16) Staatuse parameeter võimaldab teil määratleda mitmeid vastuvõetud sõnumi omadusi:

- staatus.MPI_SOURCE – auaste vastuvõetud sõnumi saatmisprotsess,

- status.MPI_TAG – vastuvõetud sõnumi silt.

17) Funktsioon

MPI_Get_count t(MPI_status *status, MPI_Datatype type, int *count)

tagastab loendusmuutujas vastuvõetud sõnumis tüüpi tüüpi elementide arvu.

18) Toimingud, mis edastavad andmeid kõikidest protsessidest ühte protsessi. Selles operatsioonis kogutud

väärtused teostavad üht või teist andmetöötlust (viimase punkti rõhutamiseks nimetatakse seda toimingut ka andmete vähendamise toiminguks)

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

19) Protsesside sünkroniseerimine, st. Arvutusprotsessi teatud punktide protsesside samaaegne saavutamine tagatakse MPI funktsiooni abil: int MPI_barrier(MPI_Comm komm); Funktsioon MPI_Barrier määratleb kollektiivse operatsiooni ja seetõttu peavad selle kasutamise korral kõik kasutatava kommunikaatori protsessid seda kutsuma. Funktsiooni MPI_Barrier kutsumisel

protsessi täitmine on blokeeritud; protsesside arvutused jätkuvad alles pärast seda, kui kõik kommunikaatori protsessid on funktsiooni MPI_Barrier kutsunud.

20) Puhverdatud edastusrežiimi kasutamiseks tuleb luua ja üle kanda MPI-mälupuhver

sõnumite puhverdamiseks – selleks kasutatav funktsioon näeb välja selline: int MPI_Buffer_attach (void *buf, int size),

- puhvermälu puhver sõnumite puhverdamiseks,

- suurus – puhvri suurus.

21) Pärast puhvriga töötamise lõpetamist tuleb see MPI-st lahti ühendada, kasutades funktsiooni:

int MPI_Buffer_detach (tühi *buf, int *suurus).

22) Andmeedastus- ja vastuvõtutoimingute tõhus ja garanteeritud samaaegne teostamine on saavutatav MPI funktsiooni abil:

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

rtype,int allikas,int rtag, MPI_Comm comm, MPI_Status *olek)

23) Kui sõnumid on sama tüüpi, on MPI-l võimalus kasutada ühte puhvrit: intMPI_Sendrecv_replace (void *buf, int count, MPI_Datatype type, int dest,

int stag, int allikas, int rtag, MPI_Comm comm, MPI_Status* olek)

24) Andmete ühest protsessist kõikidesse protsessidesse edastamise üldistatud toimimine (andmete jaotamine) erineb leviedastusest selle poolest, et protsess edastab protsessidele erinevaid andmeid (vt joonis 4.4). Seda toimingut saab teha funktsiooniga:

int MPI_Scatter (kehtetu *sbuf,int scount,MPI_Datatype tüüp,

25) Üldine andmeedastus kõigilt töötlejatelt ühte protsessi (andmete kogumine) on vastupidine andmete levitamise protseduurile (vt joonis 4.5). Selle toimingu tegemiseks MPI-s on funktsioon:

int MPI_Gather (kehtetu *sbuf,int scount,MPI_Datatype tüüp,

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

26) Tuleb märkida, et funktsiooni MPI_Gather kasutamisel kogutakse andmeid ainult

ühel protsessil. Kõigi kommunikaatoriprotsesside kohta kogutud andmete saamiseks

peate kasutama kogumise ja levitamise funktsiooni:

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

27) Andmete ülekandmine kõikidest protsessidest kõikidesse protsessidesse on kõige levinum andmeedastusoperatsioon (vt joonis 4.6). Seda toimingut saab teha funktsiooniga:

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

28) Funktsioon MPI_Reduce pakub andmete vähendamise tulemusi

ainult ühel protsessil. Kõigi kommunikaatoriprotsesside andmete vähendamise tulemuste saamiseks peate kasutama vähendamise ja levitamise funktsiooni:

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

29) Funktsiooni abil saab hankida veel ühe andmete kogumise ja töötlemise toimingu versiooni, mis tagab kõigi osaliste vähendamise tulemuste saamise:

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

Funktsiooni MPI_Scan üldine täitmisskeem on näidatud joonisel fig. 4.7. Vastuvõetud sõnumite elemendid esindavad protsesside poolt edastatud sõnumite vastavate elementide töötlemise tulemusi ja tulemuste saamist protsessi kohta, mille auaste on i, 0≤i

30) Bufpos muutuja algväärtus tuleb moodustada enne pakendamist ja seejärel määrab see funktsioon MPI_Pack. Funktsiooni MPI_Pack kutsutakse järjestikku, et pakkida kõik vajalikud andmed.

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

31) Pärast kõigi vajalike andmete pakkimist saab ettevalmistatud puhvrit kasutada andmeedastusfunktsioonides, mille tüüp on määratud MPI_PACKED.

Pärast tüübiga MPI_PACKED sõnumi saamist saab andmed lahti pakkida, kasutades funktsiooni:

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

Komplekssete juhiste komplekti arvuti

CISC (inglise keel Complex instruction set computing või inglise keele kompleksne käsukomplekti arvuti -

arvuti koos täieliku juhiste komplektiga) on protsessori disainikontseptsioon, mida iseloomustavad järgmised atribuudid:

suhteliselt väike arv üldotstarbelisi registreid;

· suur hulk masinakäske, millest mõned laaditakse semantiliselt sarnaselt kõrgetasemeliste programmeerimiskeelte operaatoritega ja täidetakse paljudes kellatsüklites;

· suur hulk adresseerimismeetodeid;

· suur hulk erineva bitisuurusega käsuvorminguid;

· kahe aadressiga käsuvormingu ülekaal;

· tüübitöötluskäskude olemasolu register-mälu.

Puudused:

riistvara kõrge hind; raskused arvutuste paralleelseerimisel.

CISC käsusüsteemi ehitustehnika on vastupidine teisele tehnikale - RISC-le. Nende mõistete erinevus seisneb programmeerimismeetodites, mitte tegelikus protsessori arhitektuuris. Peaaegu kõik kaasaegsed protsessorid emuleerivad nii RISC- kui ka CISC-tüüpi käsukomplekte.

Vähendatud juhiste komplekt arvuti

See põhineb RISC arhitektuuri põhimõtetel: fikseeritud käsuformaat, registritoimingud, käskude ühetsükliline täitmine, lihtsad adresseerimismeetodid ja suur registrifail. Samal ajal on mitmeid olulisi omadusi, mis eristavad seda arhitektuuri teiste RISC-protsessorite arhitektuuridest. Nende hulka kuuluvad: iga täiturmehhanismi sõltumatu registrikomplekt; üksikute CISC-laadsete käskude lisamine süsteemi; hilinenud ülemineku mehhanismi puudumine; originaalne viis tingimuslike hüpete rakendamiseks. Mikroprotsessor-arhitektuuride peamised rakendused on suure jõudlusega serverid ja superarvutid.

Sellised arvutid põhinesid arhitektuuril, mis eraldas töötlemisjuhised mälukäskudest ja rõhutas tõhusat konveierit. Käsusüsteem oli konstrueeritud nii, et mis tahes käsu täitmine võttis vähe masinatsükleid (soovitavalt üks masinatsükkel). Käskude täitmise loogika jõudluse suurendamiseks keskendus pigem riistvarale kui püsivara juurutamisele. Käskude dekodeerimise loogika lihtsustamiseks kasutati fikseeritud pikkusega käske

Ja fikseeritud formaat.

IN Mis on ülemineku sihtaadressi puhvertehnoloogia mõte?

IN Protsessoril on mehhanism üleminekute suuna dünaamiliseks ennustamiseks. Sellega

Kiibi sihtmärk on väike vahemälu, mida nimetatakse haru sihtpuhvriks (BTB) ja kaks sõltumatut käskude eellaadimispuhvrite paari (kaks 32-bitist puhvrit konveieri kohta). Haru sihtaadressi puhver salvestab eellaadimispuhvrites olevate juhiste aadressid. Eellaadimispuhvrite töö on korraldatud nii, et igal ajahetkel tõmmatakse käsud ainult ühte vastava paari puhvritest. Kui käsuvoos tuvastatakse haruoperatsioon, võrreldakse arvutatud haru aadressi BTB puhvris salvestatud aadressidega. Sobivuse korral eeldatakse haru toimumist ja teine ​​eellaadimispuhver on lubatud ning hakkab vastavale konveierile täitmiseks käske väljastama. Kui esineb ebakõla, eeldatakse, et haru ei käivitata ja eellaadimispuhvrit ei vahetata, jätkates tavapärast käsu andmise järjekorda. See väldib konveieri seisakuid

Struktuursed konfliktid ja viisid nende minimeerimiseks

Kombineeritud käskude täitmise režiim nõuab üldiselt funktsionaalsete üksuste konveieri ühendamist ja ressursside dubleerimist, et lahendada kõik võimalikud konveierkäskude kombinatsioonid. Kui mõni käskude kombinatsioon ebaõnnestub

ressursikonflikti tõttu aktsepteeritakse, siis väidetakse, et masinal on struktuurne konflikt. Kõige tüüpilisem näide masinatest, mille puhul võivad tekkida struktuursed konfliktid, on masinad, mille funktsionaalsed seadmed ei ole täielikult konveieritud.

Minimeerimine: konveier peatab ühe käsu täitmise, kuni vajalik seade muutub kättesaadavaks.

Andmekonfliktid, torujuhtmete seiskumised ja möödaviigumehhanismi rakendamine

Üks tegureid, mis konveiersüsteemide jõudlust oluliselt mõjutab, on käskudevahelised loogilised sõltuvused. Andmekonfliktid tekivad siis, kui konveiertöötluse kasutamine võib muuta operandikutsete järjekorda nii, et see järjekord erineb järjekorrast, mida täheldatakse käskude järjestikuse täitmisel konveierita masinas. Selles näites püstitatud probleemi saab lahendada üsna lihtsa riistvaralise tehnikaga, mida nimetatakse andmete edastamiseks, andmete möödaviimiseks või mõnikord lühistamiseks.

Andmekonfliktid põhjustavad konveieri peatamise

Selle asemel vajame näite korrektse toimimise tagamiseks täiendavat riistvara, mida nimetatakse konveieri intervaate riistvaraks. Üldiselt tuvastavad seda tüüpi seadmed konfliktid ja peatavad konveieri toimimise seni, kuni konflikt on olemas. Sel juhul peatab see riistvara konveieri, alustades käsust, mis soovib andmeid kasutada, samal ajal kui eelmine käsk, mille tulemus on meie jaoks operandi, annab selle tulemuse. See seade põhjustab tootmisliini seiskumise või "mulli" tekkimise samamoodi nagu struktuursete konfliktide korral.

Tingimuslikud haru ennustamise puhvrid

Tingimuslik haru ennustamise puhver on väike mälu, mida saab adresseerida harukäsu aadressi kõige vähem oluliste bittide abil. Iga selle mälu lahter sisaldab ühte bitti, mis näitab, kas eelmine haru käivitati või mitte. See on seda tüüpi puhvri kõige lihtsam tüüp. Sellel pole silte ja see on kasulik ainult haru latentsuse vähendamiseks juhul, kui viivitus on pikem kui haru sihtaadressi väärtuse arvutamiseks kuluv aeg. Haru ennustuspuhvrit saab rakendada väikese spetsiaalse vahemäluna, millele pääseb ligi käsuaadressi kaudu konveieri (IF) käskude toomise etapis, või bitipaarina, mis on seotud iga käsu vahemälu plokiga ja tuuakse koos iga käsuga.

Paralleliseerimine C-keeles
Näide 3b. Paralleelsus Fortranis
Näide 4a. Süsteemitaimeri omaduste määramine C-keeles
Näide 4b. Süsteemi taimeri omaduste määratlemine Fortranis

1.4. Sõnumite saatmine ja vastuvõtmine eraldi protsesside vahel

1.4.1. Punkt-punkti toimingud

1.4.2. Blokeerimisega sõnumite saatmine ja vastuvõtmine

Näide 5a. Sõnumite vahetamine kahe protsessi vahel C-keeles
Näide 5b. Sõnumite vahetamine kahe protsessi vahel Fortranis
Näide 6a. Sõnumivahetus paaris ja paaritu protsesside vahel C-s
Näide 6b. Sõnumivahetus paaris ja paaritu protsesside vahel Fortranis
Näide 7a. Edastamine olematule protsessile C-s
Näide 7b. Edastamine Fortranis olematule protsessile
Näide 8a. Puhverdatud andmete saatmine C-keeles
Näide 8b. Puhverdatud andmete saatmine Fortrani keeles
Näide 9a. Teabe hankimine sõnumi atribuutide kohta C-keeles
Näide 9b. Teabe hankimine Fortranis sõnumi atribuutide kohta
Näide 10a. Latentsuse ja läbilaskevõime määratlus C-keeles
Näide 10b. Latentsuse ja läbilaskevõime määratlemine Fortranis

1.4.3. Sõnumite saatmine ja vastuvõtmine ilma blokeerimata

Näide 11a. Vahetage ringi topoloogia kaudu, kasutades C-s mitteblokeerivaid toiminguid
Näide 11b. Vahetage ringi topoloogia kaudu, kasutades Fortranis mitteblokeerivaid toiminguid
Näide 12a. Suhtlusskeem "meister - töölised" C-keeles
Näide 12b. Suhtlusskeem "meister - töölised" Fortrani keeles
Näide 13a. Maatriksi transpositsioon C-keeles
Näide 13b. Maatriksi transponeerimine Fortranis

1.4.4. Ootel interaktsioonitaotlused

Näide 14a. Iteratiivse meetodi skeem vahetusega piki ringi topoloogiat, kasutades edasilükatud päringuid C-keeles
Näide 14b. Iteratiivse meetodi skeem rõnga topoloogia kaudu vahetamisega, kasutades Fortranis edasilükatud päringuid

1.4.5. Ummikseisud

Näide 15a. Vahetage ringi topoloogia kaudu, kasutades C-keeles protseduuri MPI_Sendrecv
Näide 15b. Vahetage ringi topoloogia kaudu, kasutades Fortranis MPI_SENDECV protseduuri

1.5. Kollektiivsete protsesside interaktsioonid

1.5.1. Üldsätted

1.5.2. Barjäär

Näide 16a. Barjääri sünkroniseerimise modelleerimine C-keeles
Näide 16b. Barjääride sünkroniseerimise modelleerimine Fortranis

1.5.3. Kollektiivsed andmeedastustoimingud

1.5.4. Globaalsed operatsioonid

Näide 17a. Globaalse liitmise modelleerimine kahekordistusskeemi ja kollektiivoperatsiooni MPI_Reduce abil C-keeles
Näide 17b. Globaalse summeerimise modelleerimine kahekordistusskeemi ja kollektiivoperatsiooni MPI_Reduce abil Fortranis

1.5.5. Kohandatud globaalsed toimingud

Näide 18a. Kohandatud globaalne funktsioon C-keeles
Näide 18b. Kohandatud globaalne funktsioon Fortranis

1.6. Rühmad ja suhtlejad

1.6.1. Üldsätted

1.6.2. Toimingud protsessirühmadega

Näide 19a. Töö gruppidega C-keeles
Näide 19b. Töö gruppidega Fortranis

1.6.3. Toimingud suhtlejatega

Näide 20a. Kommunikaatori purunemine C-s
Näide 20b. Kommunikaatori jagamine Fortranis
Näide 21a. Protsesside nummerdamine C-keeles
Näide 21b. Nummerdamise protsessid Fortranis

1.6.4. Suhtlejad

Näide 22a. Meister-töötaja skeem, mis kasutab C-keeles suhtlejat
Näide 22b. Meister-tööline vooluring, kasutades sidevahendit Fortranis

1.6.5. Atribuudid

1.7. Virtuaalsed topoloogiad

1.7.1. Üldsätted

1.7.2. Descartes'i topoloogia

1.7.3. Graafi topoloogia

Näide 23a. Master-worker diagramm, kasutades graafi topoloogiat C-keeles
Näide 23b. Master-worker skeem, kasutades graafi topoloogiat Fortranis

1.8. Erinevat tüüpi andmete saatmine

1.8.1. Üldsätted

1.8.2. Tuletatud andmetüübid

Näide 24a. Maatriksi veergude ümberpaigutamine vastupidises järjekorras C-keeles
Näide 24b. Maatriksi veergude ümberpaigutamine Fortranis vastupidises järjekorras

1.8.3. Andmete pakkimine

Näide 25a. Pakitud andmete saatmine C-keeles
Näide 25b. Pakitud andmete saatmine Fortranis

1.9. infoobjekt

1.9.1. Üldsätted

1.9.2. Infoobjektiga töötamine

1.10. Dünaamiline protsessi juhtimine

1.10.1. Üldsätted

1.10.2.Protsesside loomine

meister.c
ori.c
Näide 26a. Master-worker skeem kasutades protsessi kudemist C-keeles
meister.f
ori.f
Näide 26b. Master-worker skeem, mis kasutab protsesside kudemist Fortranis

1.10.3. Kliendi-serveri suhtlus

server.c
klient.c
Näide 27a. Andmevahetus serveri ja kliendi vahel, kasutades C-keeles avalikku nime
server.f
klient.f
Näide 27b. Andmevahetus serveri ja kliendi vahel, kasutades avalikku nime Fortrani keeles

1.10.4. Protsessi seose eemaldamine

1.10.5. Pistikupesa side

1.11. Ühesuunaline side

1.11.1. Üldsätted

1.11.2. Aknaga töötamine

1.11.3. Andmete ülekanne

1.11.4. Sünkroonimine

Näide 28a
Näide 28b
Näide 29a. Vahetage ringi topoloogia kaudu, kasutades C-s ühesuunalist sidet
Näide 29b. Vahetage ringi topoloogia kaudu, kasutades Fortranis ühesuunalist sidet
Näide 30a. Vahetage ringi topoloogia kaudu, kasutades C-s ühesuunalist sidet
Näide 30b. Vahetage ringi topoloogia kaudu, kasutades Fortranis ühesuunalist sidet

1.12. Välised liidesed

1.12.1. Üldised päringud

1.12.2. Teave staatusest

1.12.3. Niidid

1.13. Paralleelne I/O

1.13.1. Definitsioonid

1.13.2. Töötamine failidega

1.13.3. Juurdepääs andmetele

Näide 31a. Puhverdatud lugemine failist C-keeles
Näide 31b. Puhverdatud lugemine failist Fortranis
Näide 32a. Kollektiivne lugemine failist C-keeles
Näide 32b. Kollektiivne lugemine failist Fortranis

1.14. Viga töötlemisel

1.14.1. Üldsätted

1.14.2. Kommunikaatoritega seotud veakäsitlejad

1.14.3. Akendega seotud veakäsitlejad

1.14.4. Failidega seotud veakäsitlejad

1.14.5. Täiendavad protseduurid

1.14.6. Veakoodid ja klassid

1.14.7. Helistamine veakäsitlejatele

Näide 33a. C-keeles vigade käsitlemine
Näide 33b. Vigade käsitlemine Fortranis

2. peatükk OpenMP paralleelprogrammeerimise tehnoloogia

2.1. Sissejuhatus

2.2. Põhimõisted

2.2.1. Programmi koostamine

Näide 34a. Tingimuslik koostamine keeles C
Näide 34b
Näide 34c. Tingimuslik koostamine Fortranis

2.2.2. Paralleelprogrammi mudel

2.2.3. direktiivid ja menetlused

2.2.4. Programmi täitmine

2.2.5. Ajastus

Näide 35a. Süsteemi taimeritega töötamine C-vormingus
Näide 35b. Süsteemi taimeritega töötamine Fortranis

2.3. Paralleelsed ja jadapiirkonnad

2.3.1. paralleeldirektiiv

Näide 36a. Rööppiirkond C-keeles
Näide 36b. Paralleelne piirkond Fortranis
Näide 37a. Vähendamise võimalus C-keeles
Näide 37b. Vähendamise võimalus Fortranis

2.3.2. Lühikiri

2.3.3. Keskkonnamuutujad ja abistamisprotseduurid

Näide 38a. Protseduur Omp_set_num_threads ja suvand num_threads C-keeles
Näide 38b. Protseduur omp_set_num_threads ja valik num_threads Fortrani keeles
Näide 39a. Protseduurid omp_set_dynamic ja omp_get_dynamic C-keeles
Näide 39b. Protseduurid omp_set_dynamic ja omp_get_dynamic Fortranis
Näide 40a. Pesastatud paralleelsed piirkonnad C-s
Näide 40b. Pesastatud paralleelsed piirkonnad Fortranis
Näide 41a. Funktsioon Omp_in_parallel C-keeles
Näide 41b. Funktsioon omp_in_parallel Fortrani keeles

2.3.4. ühtne direktiiv

Näide 42a. Üksikdirektiiv ja nowait-valik C-keeles
Näide 42b. Üksikdirektiiv ja nowait valik Fortranis
Näide 43a. Copyprivate valik C-keeles
Näide 43b. copyprivate võimalus Fortranis

2.3.5. põhidirektiiv

Näide 44a. Põhidirektiiv C-keeles
Näide 44b. põhidirektiiv Fortranis

2.4. Andmemudel

Näide 45a. Privaatne valik C-keeles
Näide 45b. Privaatne valik Fortranis
Näide 46a. Jagatud valik C-keeles
Näide 46b. Jagatud valik Fortranis
Näide 47a. esimene privaatne valik C-keeles
Näide 47b. esimene privaatne võimalus Fortranis
Näide 48a. eradirektiiv C-keeles
Näide 48b. niit eradirektiiv Fortranis
Näide 49a. Kopeerimise võimalus C-keeles
Näide 49b. kopeerimise võimalus Fortranis

2.5. Tööde jaotus

2.5.1. Madala taseme paralleelsus

Näide 50a. Protseduurid omp_get_num_threads ja omp_get_thread_num C-keeles
Näide 50b. Protseduurid omp_get_num_threads ja omp_get_thread_num Fortranis

2.5.2. Paralleelsed silmused

Näide 51a. direktiivi jaoks C-keeles
Näide 51b. Do-direktiiv Fortranis
Näide 52a. Ajakava valik C-keeles
Näide 52b. ajakava valik Fortranis
Näide 53a. Ajakava valik C-keeles

Juhtus nii, et mul oli lähedalt kokkupuude paralleelarvutuse ja eriti MPI uurimisega. Võib-olla on see suund täna väga paljutõotav, nii et ma tahaksin sirvijatele näidata selle protsessi põhitõdesid.

Põhiprintsiibid ja eeskuju
Näitena kasutatakse eksponentsiaali (e) arvutamist. Üks selle leidmise võimalustest on Taylori seeria:
e^x=∑((x^n)/n!), kus summeerimine toimub vahemikus n=0 kuni lõpmatuseni.

Seda valemit saab hõlpsasti paralleelstada, kuna vajalik arv on üksikute terminite summa ja tänu sellele saab iga protsessor hakata individuaalseid termineid arvutama.

Igas üksikus protsessoris arvutatavate terminite arv sõltub nii intervalli n pikkusest kui ka arvutusprotsessis osaleda saavate protsessorite k arvust. Seega, kui näiteks intervalli pikkus on n=4 ja arvutustesse on kaasatud viis protsessorit (k=5), siis esimesest kuni neljandani protsessorid saavad igaüks ühe liikme ja viiendat ei kasutata. Kui n=10 ja k=5, saab iga protsessor arvutamiseks kaks liiget.

Esialgu saadab esimene protsessor MPI_Bcast levifunktsiooni kasutades teistele kasutaja määratud muutuja n väärtuse. Üldiselt on funktsioonil MPI_Bcast järgmine vorming:
int MPI_Bcast(void *buffer, int count, MPI_Datatype andmetüüp, int root, MPI_Comm comm), kus puhver on puhvri aadress koos elemendiga, count on elementide arv, andmetüüp on MPI-s vastav andmetüüp, root on edastamist haldava põhiprotsessori auaste ja komm on kommunikaatori nimi.
Minu puhul on põhiprotsessori roll, nagu juba mainitud, esimene protsessor, mille auaste on 0.

Pärast numbri n edukat saatmist hakkab iga protsessor oma tingimusi arvutama. Selleks lisatakse tsükli igal etapil arvule i arv, mis võrdub arvutustes osalevate protsessorite arvuga, mis on algselt võrdne protsessori auastmega. Kui järgmiste sammude arv i ületab kasutaja määratud arvu n, peatub selle protsessori tsükli täitmine.

Tsükli täitmisel lisatakse terminid eraldi muutujasse ja pärast selle täitmist saadetakse saadud summa põhiprotsessorisse. Selleks kasutatakse vähendamise operatsiooni funktsiooni MPI_Reduce. Üldiselt näeb see välja selline:
int MPI_Reduce(void *buf, void *result, int count, MPI_Datatype andmetüüp, MPI_Op op, int root, MPI_Comm comm)

See ühendab rühma iga protsessi sisendpuhvri elemendid, kasutades operatsiooni operatsiooni ja tagastab kombineeritud väärtuse protsessinumbri juure väljundpuhvrisse. Sellise toimingu tulemuseks on üks väärtus, mistõttu casting-funktsioon sai oma nime.

Pärast programmi käivitamist kõigil protsessoritel saab esimene protsessor terminite kogusumma, mis on meile vajalik eksponendi väärtus.

Tuleb märkida, et nii paralleel- kui ka järjestikuse eksponendi arvutamise meetodi puhul kasutatakse faktoriaali leidmiseks rekursiivset funktsiooni. Otsustades, kuidas teostatavat ülesannet paralleelselt teha, kaalusin varianti leida faktoriaal ka erinevatel protsessoritel, kuid lõpuks lugesin seda võimalust minu poolt irratsionaalseks.

Esmane ülesanne on ikkagi astendaja väärtuse leidmine ja kui protsessorid hakkavad iga liikme iga faktoriaali eraldi arvutama, võib see kaasa tuua täpselt vastupidise efekti, nimelt jõudluse ja arvutuskiiruse olulise vähenemise.
Seda seletatakse asjaoluga, et sellisel juhul tekib väga suur koormus suhtluskeskkonnale, mis on paralleelarvutussüsteemides juba sageli nõrk lüli. Kui faktoriaal arvutatakse igal protsessoril privaatselt, on sideliinide koormus minimaalne. Seda juhtumit võib nimetada heaks näiteks sellest, et ka paralleelstamise ülesandel peavad vahel olema piirid.

Koodi täitmise algoritm
1. Arvu n väärtus kantakse visuaalsest kestast programmi, mis seejärel saadetakse levifunktsiooni kasutades kõikidele protsessoritele.
2. Kui esimene põhiprotsessor on lähtestatud, käivitub taimer.
3. Iga protsessor käivitab tsükli, kus juurdekasvu väärtus on protsessorite arv süsteemis. Silmuse igal iteratsioonil arvutatakse liige ja selliste terminite summa salvestatakse muutujasse drobSum.
4. Pärast tsükli lõppemist lisab iga protsessor oma drobSum väärtuse muutujale Result, kasutades redutseerimisfunktsiooni MPI_Reduce.
5. Pärast kõigi protsessorite arvutuste lõpetamist peatab esimene põhiprotsessor taimeri ja saadab muutuja Result saadud väärtuse väljundvoogu.
6. Väljundvoogu saadetakse ka meie taimeri poolt mõõdetud ajaväärtus millisekundites.
Koodide loend
Programm on kirjutatud C++ keeles, eeldame, et käivitamise argumendid edastatakse välisest kestast. Kood näeb välja selline:
#kaasa "mpi.h"
#kaasa
#kaasa
kasutades nimeruumi std;

topeltfakt(int n)
{
kui (n==0)
tagastus 1;
muidu
tagasta n*Fact(n-1);
}

int main(int argc, char *argv)
{
SetConsoleOutputCP(1251);
int n;
int myid;
int numprocs;
int i;
int rc;
pikk double drob,drobSum=0,Tulemus,summa;
topelt algusaeg = 0,0;
topelt lõpuaeg;

N = atoi(argv);

if (rc= MPI_Init(&argc, &argv))
{
cout<< "Käivitusviga, täitmine peatatud" << endl;
MPI_Abort(MPI_COMM_WORLD, rc);
}

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

kui (minuid == 0)
{

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

jaoks (i = myid; i<= n; i += numprocs)
{
drob = 1/Fakt(i);
drobSum += drob;
}

MPI_Reduce(&drobSum, &Result, 1, MPI_LONG_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
cout.precision(20);
kui (minuid == 0)
{
cout<< Result << endl;
lõpuaeg = MPI_Wtime();
cout<< (endwtime-startwtime)*1000 << endl;
}

MPI_Finalize();
tagasi 0;
}


* See lähtekood tõsteti esile Source Code Highlighteriga.
Järeldus
Nii saime lihtsa programmi eksponendi arvutamiseks, kasutades korraga mitut protsessorit. Tõenäoliselt on kitsaskohaks tulemuse salvestamine ise, sest numbrite arvu suurenemisega ei muutu standardtüüpide abil väärtuse salvestamine triviaalseks ja see koht nõuab täpsustamist. Võib-olla on üsna ratsionaalne lahendus kirjutada tulemus faili, kuigi selle näite puhtalt harivat funktsiooni silmas pidades pole vaja sellele erilist tähelepanu pöörata.