Programmeren aan Parallel WAQUA/TRIWAQ Cursusboek Module 3

Maat: px
Weergave met pagina beginnen:

Download "Programmeren aan Parallel WAQUA/TRIWAQ Cursusboek Module 3"

Transcriptie

1 Programmeren aan Parallel WAQUA/TRIWAQ Cursusboek Module 3

2 Programmeren aan Parallel WAQUA/TRIWAQ Cursusboek Module 3 Version : 1.0, oktober 2002 Maintenance : see Copyright : Rijkswaterstaat

3 Inhoud PROGRAMMEREN AAN PARALLEL WAQUA/TRIWAQ...I CURSUSBOEK MODULE 3...I 1 INTRODUKTIE PARALLEL PROGRAMMEREN ONDERDELEN VAN PARALLEL PROGRAMMEREN PARALLELLE COMPUTER ARCHITECTUREN PARALLELLE PROGRAMMEERPARADIGMAS PROBLEEM DECOMPOSITIE STRATEGIËN CLASSIFICATIE VAN REKENPROBLEMEN ANALYSEREN VAN PARALLELLISME EN DATA- AFHANKELIJKHEDEN GLOBAAL ONTWERP VAN PARALLEL WAQUA/TRIWAQ UITGANGSPUNTEN BIJ DE PARALLELLISATIE OVERZICHT VAN DE SYSTEEM-STRUCTUUR UITWERKING VAN DE ROOSTER-OPDELING IMPLEMENTATIE VAN NUMERIEKE BEREKENINGEN ( LOOPS ) IN WAQUA/TRIWAQ PARALLELLISATIE VAN NUMERIEKE BEREKENINGEN ( LOOPS ) IN WAQUA/TRIWAQ NUMERIEKE ASPECTEN AANPASSINGEN AAN SOLVERS I AANPASSINGEN AAN SOLVERS II VEREISTE HERSTRUCTURERING VAN DE SEQUENTIËLE PROGRAMMATUUR PARALLELLISATIE VAN CONVERGENTIE-CRITERIA VOORTGEZETTE DATA-ANALYSE THEORIE EN PRAKTIJK VAN COMMUNICATIE MET COCLIB OVERZICHT VAN COCLIB COCLIB EN COEXEC INDEXSETS EN STENCILS DE COCLIB ROUTINES GEBRUIK VAN GLOBALE COMMUNICATIES UITBREIDINGEN VOOR DOMEIN DECOMPOSITIE BASISFILOSIFIE INTERPOLATIE IN COCLIB INTERFACES BIJ HORIZONTALE VERFIJNING INTERNE WERKING VAN COEXEC DE FUNCTIES VAN COEXEC IN- EN UITVOER VAN COEXEC Versie 1.0, oktober

4 6.3 UITGEVOERDE CHECKS HET OPSTARTEN VAN PROCESSEN BOODSCHAPPEN TUSSEN WAQPRO EN COEXEC INTERNE WERKING VAN DE PARTITIONER COPPRE DE FUNCTIES VAN COPPRE INVOER EN UITVOER VAN COPPRE DE WERKING VAN COPPRE TOEVOEGEN VAN NIEUWE DATA STRUCTUREN INTERNE WERKING VAN DE COLLECTOR COPPOS FUNCTIES VAN COPPOS HET COLLECTIE ALGORITME SPECIALE GEVALLEN CASE-STUDY: PARALLELISATIE VAN HET HORIZONTAAL K- MODEL REFERENTIES Versie 1.0, oktober

5 1 Introduktie Parallel Programmeren Voorwoord: het doel van module 3 is om inzicht te geven in de manier waarop parallel rekenen en domein decompositie in WAQUA/TRIWAQ zijn geïmplementeerd. Na afloop van de cursus zouden de deelnemers de gehanteerde aanpak globaal moeten kunnen plaatsen ten opzichte van mogelijke andere aanpakken, de consequenties van parallel rekenen en domein decompositie kunnen beoordelen voor wijzigingen aan WAQUA/TRIWAQ, en voor een aantal gevallen de parallellisatie van nieuwe functionaliteit zelf kunnen uitvoeren. In dit eerste hoofdstuk geven we een overzicht van wat het in het algemeen inhoudt om programmatuur te ontwikkelen voor parallelle computers. Daarbij ligt de nadruk op de gehanteerde aanpak voor parallellisatie van WAQUA/TRIWAQ, maar wordt deze aanpak ook in een bredere context geplaatst. 1.1 Onderdelen van parallel programmeren Hoewel parallel rekenen op grote schaal succesvol wordt toegepast bestaat er niet een enkele manier voor parallel programmeren. In plaats daarvan zijn er vele verschillende programmeertalen die kunnen worden gebruikt (honderden), ieder met hun eigen sterke en zwakke kanten. De verschillende alternatieven zijn moeilijk met elkaar vergelijkbaar doordat ze zijn gebaseerd op sterk verschillende uitgangspunten, die vaak voortkomen uit verschillende toepassingsgebieden waarop men zich richt, of verschillende soorten parallelle computers die men wil kunnen gebruiken. In deze cursus richten we ons op numerieke simulatieproblemen. In deze problemen staat de te gebruiken oplosmethode vaak centraal: simulatieprogramma s zijn gericht op het uitvoeren van een bepaalde taak, en die taak wordt beschreven door de stappen die er moeten worden uitgevoerd. Ook binnen deze klasse van problemen blijkt er een veelheid aan methoden te zijn voor parallel rekenen, en blijken methoden vanuit allerlei verschillende invalshoeken te kunnen worden bekeken. Er is dan ook geen enkel recept te geven voor het parallelliseren van een applicatie. Wel kunnen we een vijftal belangrijke aspecten van parallel rekenen identificeren: 1) Bepalen van het parallellisme in de gestelde rekentaak, d.w.z. welke berekeningen in principe gelijktijdig kunnen worden uitgevoerd, en eventueel vergroten van het parallellisme door veranderen van het gebruikte algoritme. 2) Classificeren van het soort parallellisme: afhankelijk van de structuur en regelmaat van de berekeningen hebben bepaalde methoden voor parallellisatie Versie 1.0, oktober

6 duidelijke voordelen of kunnen ze juist direct worden uitgesloten. 3) Kiezen van een mechanisme voor het verdelen van de berekeningen, bijvoorbeeld op basis van de verschillende soorten berekeningen (stroming vs. stoftransport) of op basis van de data-objecten van het probleem. 4) Kiezen van een programmeerparadigma. Dit is min of meer de software architectuur: de programmeertaal, - omgeving, en de structuur van het programma of de programma s. 5) Kiezen van de parallelle computer(s) waarop de berekening moet kunnen worden uitgevoerd. Bij dit laatste punt aangekomen blijkt direct dat de punten niet in de gegeven volgorde kunnen uitgewerkt: de te gebruiken hardware kan een grote invloed hebben op de te gebruiken programmeertaal, probleemopdeling en zelfs de numerieke algoritmes die men wil gebruiken. Daarom geeft bovenstaande geen recept maar een soort checklist van onderdelen die een goede parallellisatiestrategie bevat. De vijf aspecten moeten goed van elkaar worden onderscheiden en voor alle aspecten moeten duidelijke keuzes worden gemaakt. In de volgende paragrafen worden de onderdelen een voor een uitgewerkt, te beginnen met de laatste. In het volgende hoofdstuk wordt dit stramien gebruikt voor het beschrijven van de parallellisatiestrategie die voor WAQUA/TRIWAQ is gebruikt. 1.2 Parallelle computer architecturen In de afgelopen jaren is er een sterke convergentie geweest in de architecturen van commercieel succesvolle parallelle computers. Eigenlijk kan men stellen dat alle parallelle computers tegenwoordig zijn opgebouwd uit een aantal onafhankelijke volledig functionele processoren die met elkaar zijn verbonden door een krachtig interconnectie-netwerk en die zijn voorzien van een grote hoeveelheid geheugen en schijfruimte. Dit in tegenstelling tot begin jaren negentig van de vorige eeuw, toen er een groot verschil was tussen (parallelle) vectorcomputers (Cray YMP, C90), clusters van volledig functionele processoren (Ncube, Intel Paragon, Transputers), en machines waarin er een veel groter aantal zeer eenvoudige processing elements werden gebruikt (Maspar-I, CM-5). De toenmalig gangbare classificatie van computers als SIMD of MIMD (single of multiple instruction, multiple data) is voor parallel rekenen van minder belang geworden, maar is nog wel relevant voor speciale processoren zoals voor grafische bewerkingen. Ook de structuur van de gebruikte interconnectie- Versie 1.0, oktober

7 netwerken (ring, mesh, torus, hypercubus) is tegenwoordig een stuk minder relevant door de sterke verbetering van routingchips, operating systemen (bijv. virtueel shared memory) en communicatie-bibliotheken (PVM, MPI). Toch zijn er nog de nodige verschillen tussen parallelle computers die van belang zijn voor het parallel programmeren. Deze verschillen zitten vooral in de hierarchische opbouw van het geheugen en in de snelheid van het interconnectie-netwerk. Computergeheugens zijn meestal hierarchisch georganiseerd omdat er geheugen van verschillende snelheden wordt toegepast: een klein beetje zeer snel en kostbaar geheugen en een grote hoeveelheid langzaam en goedkoper geheugen. Het snelste geheugen bestaat uit de registers van de processoren zelf. Daarna komen het cache -geheugen en het centrale geheugen, en tenslotte worden ook harde schijven als geheugen gebruikt (swappen, out of core rekenen). Cache geheugen wordt gebruikt om gegevens en programma-instructies die mogelijk veel keer achter elkaar nodig zijn dicht bij de processor te bewaren. Op basis van de geheugenstructuur kunnen parallelle computers in de volgende drie categoriën worden onderverdeeld: Symmetric multiprocessors (SMP). Dit zijn computers waarin alle processoren even snel bij alle stukken van het centrale geheugen kunnen komen. Bijvoorbeeld dual-pentium PC s bevatten een enkele systeembus waar aan de ene kant de twee processoren op zijn aangesloten en aan de andere kant het centrale geheugen ligt. Vergelijkbare principes worden toegepast in multiprocessor werkstations zoals de HP K460, SUN HPC450 en SGI Onyx. NUMA multiprocessors, ofwel Virtually (distributed) shared memory computers (VSM, DSM). Dit zijn computers die zijn opgebouwd uit meerdere processoren die wel allemaal bij alle stukken van het centrale geheugen kunnen komen, maar waarbij de toegangstijden variëren voor verschillende stukken (NUMA=non-uniform memory access). Bijvoorbeeld doordat de machine fysiek is op gebouwd uit processormemory combinaties die met elkaar zijn verbonden via een interconnectie-netwerk, en waar de systeemhardware of software ervoor zorgt dat de geheugens van andere processoren toegankelijk zijn. Typische voorbeelden van deze classe zijn de SGI Origin2000/3800 series (Unite, Teras) en de Cray T3E. Multicomputers parallelle computers die bestaan uit afzonderlijke processor-memory combinaties die zijn verbonden via een of ander netwerk, en waarbij iedere Versie 1.0, oktober

8 processor alleen zijn eigen geheugen kan benaderen. De processor-memory combinaties kunnen zijn geïntegreerd in een enkele machine, zoals bij de IBM SP2, maar vaker worden parallelle computers van dit type gevormd door het met elkaar verbinden van afzonderlijke sequentiële of parallelle computers. Clusters van Linux PC s vallen in deze categorie. Deze worden ook wel netwerken van werkstations genoemd (NOW), en als er voornamelijk standaard hardware wordt gebruikt dan wordt ook de benaming Beowulf cluster toegepast. De verschillen tussen deze architecturen hebben enkele belangrijke consequenties voor de manier waarop men ze kan programmeren. Op multicomputers is geen gezamelijk geheugen beschikbaar, dus moet men altijd enige vorm van message passing gebruiken (zie volgende paragraaf), terwijl op SMP s ook een shared memory benadering mogelijk is. Op NUMA multiprocessors zijn in theorie beide aanpakken mogelijk. Maar in de praktijk eisen de verschillende geheugensnelheden vaak toch dat de programmeur zelf in de gaten houdt wanneer welke gegevens worden uitgewisseld. Hoewel de communicatie misschien niet hoeft te worden geprogrammeerd heeft dit toch veel weg van de message passing benadering. De snelheid van het interconnectie-netwerk van de parallelle computer variëert van supersnel (bus of andere structuur in een SMP-machine) tot erg langzaam (samen gebruikte supercomputers van verschillende rekencentra). In veel gevallen is de communicatietijd voor een boodschap goed te benaderen met een opstarttijd (latency) en een bandbreedte (1/tijd per byte). Dit onafhankelijk van de werkelijke afstand tussen processoren in het netwerk. In een mesh-structuur zou je verwachten dat het aantal hops zou meewegen in de communicatietijd, maar tegenwoordig blijken dit soort effecten van ondergeschikt belang. Gangbare getallen voor latency en bandbreedte zijn 800 s en 5MB/sec voor een Linux cluster met fast-ethernet (100Mbit/sec), en 15 s, 150MB/sec voor de TERAS machine van SARA. 1.3 Parallelle programmeerparadigmas Een programmeerparadigma is een stel van regels dat men kiest voor het structureren van een programmeertaak. Bijvoorbeeld de programmeertaal die men kiest en de conventies die men daarbij afspreekt voor het vormgeven van modules. Ook de structurering van de programmatuur hoort erbij: het executiemodel, de onafhankelijke eenheden van het programma (processen, threads, objecten). Veel van deze aspecten horen ook bij de gebruikte software architectuur. Versie 1.0, oktober

9 Het is belangrijk om te beseffen dat er verschillende programmeerparadigmas mogelijk zijn en om het gebruikte paradigma goed te beschrijven. De keuze voor een paradigma heeft grote invloed op verschillende kwaliteitsaspecten van de uiteindelijke programmatuur (onderhoudbaarheid, uitbreidbaarheid, performance). De beschrijving van het paradigma geeft veel houvast bij de implementatie van de programmatuur. Het eerste onderscheid tussen verschillende programmeerparadigmas dat van belang is voor parallel rekenen is het onderscheid tussen impliciet en expliciet parallel programmeren. Met impliciet parallel programmeren wordt bedoeld dat de programmeur zich op een vrij abstract niveau bezig houdt met het programma zonder concreet te denken in termen van de rekenprocessen die het uiteindelijke werk gaan doen. Een eerste vorm hiervan is dat de programmeur een sequentiëel programma maakt en dat de parallellisatie automatisch gebeurt door een parallelliserende compiler. Dit gaat moeizaam als de programmeur geen rekening heeft gehouden met parallellisatie ( legacy Fortran77-code) en levert dan vaak slechts een beperkte versnelling op. Het gaat beter als de programmeur het parallellisme in zijn programma meer zichtbaar maakt voor de compiler. Dit kan door toevoegen van compiler-directives, of bijvoorbeeld door het gebruik van data parallelle statements: array-syntax, forall -loops, etc. Dit is in het bijzonder sterk uitgewerkt in de programmeertaal High Performance Fortran (HPF), welke tegenwoordig is opgenomen in Fortran95. De data parallelle benadering is vooral voor regelmatige problemen goed geschikt. Een andere vorm van impliciet parallel programmeren is bijvoorbeeld dat de programmeur de code schrijft voor een heleboel objecten die met elkaar interacteren zonder erbij te bepalen hoe deze over de beschikbare processoren moeten worden verdeeld. Nog een andere vorm is de benadering waarin de programmeur programma-code schrijft voor aparte taken en hun volgorde-afhankelijkheden geeft (taakgraaf), maar het daadwerkelijk uitvoeren van de taken overlaat aan het systeem. Expliciet parallel programmeren is dat de programmeur zich sterk bezig houdt met de daadwerkelijke rekenprocessen die uiteindelijk worden uitgevoerd, inclusief hun onderlinge interacties. Aangezien het managen van meerdere processen extra complicaties met zich meebrengt heeft dit natuurlijk niet de voorkeur. Maar in de praktijk blijkt de extra complexiteit redelijk te kunnen worden beheerst, en blijken impliciete benaderingen veelal niet afdoende voor het bereiken van tevredenstellende performance. Versie 1.0, oktober

10 Het belangrijkste verschil tussen verschillende methoden voor expliciet parallel programmeren is hoe de verschillende processen met elkaar interacteren. De twee extremen hierbij zijn als volgt: Shared data paradigma alle rekenprocessen werken op een enkele globale data-ruimte. Alle processen kunnen in principe alle variabelen uit die data-ruimte opvragen zonder tussenkomst van andere processen. Binnen dit paradigma speelt synchronisatie van de berekeningen van verschillende processen een belangrijke rol. Process/channel paradigma ieder proces werkt op een eigen lokale data-ruimte die niet direct toegankelijk is voor de andere processen. De uitwisseling van gegevens tussen de processen is expliciet zichtbaar in hun programmas. Hiervoor worden channels gebruikt: deze zorgen voor ontkoppeling van welke data er gecommuniceerd wordt en met wie er gecommuniceerd wordt. Deze twee methoden kunnen ook shared memory programming en message passing programming worden genoemd. Maar dat is enigszins misleidend omdat het process/channel paradigma net zo goed op een shared memory computer kan worden toegepast, terwijl het shared data model ook op een (distributed memory) multicomputer kan worden ondersteund. Verder kleven aan de termen shared memory en message passing connotaties als low level en moeilijk, terwijl dit bij gebruik van een geschikte software-omgeving allerminst het geval hoeft te zijn. De voor grootschalige rekenproblemen meest relevante programmeeromgevingen die bij bovenstaande paradigmas horen zijn respectievelijk OpenMP en MPI. Dit zijn beiden standaarden die door een breed forum zijn ontwikkeld en worden ondersteund. OpenMP is een set van compiler-directives en library-functies waarmee een sequentiëel Fortran of C-programma kan worden geparallelliseerd binnen het shared data paradigma. Het executiemodel dat hierbij wordt gebruikt is dat er in eerste instantie een enkele thread wordt opgestart voor het programma, maar dat er voor verschillende secties van de code meerdere threads kunnen worden gebruikt. De programmeur geeft aan welke secties dat zijn, en hoe de threads aan het begin en einde van deze secties moeten synchroniseren. De variabelen van het programma zijn in principe globaal beschikbaar voor alle verschillende threads, maar kunnen door de programmeur als thread-local worden gedeclareerd. Communicatie gebeurt impliciet via de variabelen die de verschillende processen met elkaar delen. OpenMP is primair gericht op de single program multiple data (SPMD) manier Versie 1.0, oktober

11 van programmeren: een enkel programma dat meerdere keren wordt opgestart voor verschillende sets van invoer-data. MPI is een bibliotheek van communicatiefuncties die in Fortran en C-programmas kunnen worden gebruikt. Hier worden er meerdere instanties van een enkele executable tegelijk opgestart en deze gaan dan al communicerend samen een probleem oplossen. Maar ook kunnen er onderweg nog nieuwe processen worden opgestart, men is dus niet beperkt tot het SPMD-model. Er is een groot aantal communicatie-routines waarmee een proces data kan versturen naar en ontvangen van andere processen, evenals routines waarmee kan worden gecommuniceerd met groepen van andere processen. De synchronisatie van de processen gebeurt impliciet op de momenten dat ze wachten op boodschappen van de anderen. Het grootste verschil tussen de shared data en process/channel paradigmas zit in de manier waarop data is gekoppeld aan processen. In het process/channel paradigma is er een sterke koppeling, zijn de gegevens op ieder moment het exclusieve bezit van een enkel proces. Hierdoor zijn de gegevens in de geheugenhierarchie steeds dicht bij de processor die ze nodig heeft, is er een goede data localiteit. Dit is belangrijk voor het bereiken van goede performance. Verder zijn zulke programmas gemakkelijker te analyseren en debuggen, interacties tussen verschillende processen kunnen niet gemakkelijk over het hoofd worden gezien (de programmeur heeft niet te maken met anti-dependencies en race conditions, zie paragraaf 1.6). In het shared data paradigma is de koppeling tussen data en processen minder strak. De programmeur hoeft daardoor niet precies te vertellen welke gegevens er moeten worden gecommuniceerd, waardoor dit model in het algemeen als gemakkelijker te gebruiken wordt ervaren. Verder is dit voordelig als de werkverdeling dynamisch moet worden aangepast of als het probleem minder regelmatig is. Dit echter ten koste van een stuk controle over de performance, en ten koste van de overdraagbaarheid naar computers zonder een (virtueel) shared memory systeem. Tenslotte noemen we paar bekende structuren van samenwerkende processen die ook als onderdeel van het paradigma worden beschouwd. Dit zijn de farmer-worker organisatie, ook wel master-slave en process-farm genoemd, waarbij een coördinerend proces steeds taken uitdeelt aan de werker-processen, de pipeline organisatie, waarbij ieder proces zijn eigen bewerking uitvoert op een stel gegevens en deze dan doorgeeft aan het volgende proces in de rij, en de agenda-parallelle manier van werken, waarbij alle rekenprocessen dezelfde agenda van activiteiten uitvoeren en daarin steeds hun eigen gedeelte uitvoeren. Dit laatste is min of meer gelijk aan de SPMD benadering. Versie 1.0, oktober

12 1.4 Probleem decompositie strategiën Het volgende aspect van parallel programmeren dat we bespreken is de strategie die wordt gevolgd voor het verdelen van het rekenwerk in afzonderlijke taken. Drie generieke mechanismes die hiervoor kunnen worden gebruikt zijn het verdelen van de data van het probleem, het verdelen van de operaties van het probleem, of het verdelen van taken, operaties plus data, van het probleem. Het verdelen van de data van een probleem is een aantrekkelijke methode omdat er in het algemeen bij grote rekenproblemen ook grote aantallen verschillende data-items worden gebruikt, en omdat er vaak sprake is van een enkele agenda van super-stappen, berekeningen voor een groot aantal data-items tegelijkertijd. Het principe van datadecompositie wordt ook wel data parallellisme genoemd, of geometrische decompositie (als de data een ruimtelijke betekenis hebben), rooster-opdeling (als niet de data-items zelf maar een onderliggend abstract rooster wordt verdeeld) of domein decompositie. Relevante aspecten van een datadecompositie zijn of deze statisch is of dynamisch moet variëren, en of alle delen gelijk of onderling verschillend zijn. Het verdelen van de operaties van een probleem wordt veelal functioneel parallellisme genoemd. Dit heeft vooral toepassingen in pipelines produktielijnen waar steeds dezelfde serie operaties moet worden uitgevoerd op verschillende hoeveelheden data. Het verdelen van alle berekeningen in een stel taken met volgorde-restricties (taakgraaf) is praktisch omdat het nauw aansluit bij het analyseren van het parallellisme in een berekening (zie paragraaf 1.6). Met name als de hoeveelheid werk per taak moeilijk van te voren is in te schatten. Denk bijvoorbeeld aan een directe solver voor een in blokken gepartitioneerde ijle matrix: het is gemakkelijk om taken aan te wijzen (vegen blokken) en hun volgorde-relaties te specificeren, en het is een stuk moeilijker om een geschikte data-decompositie van het probleem te maken (verdeling van de matrix of van de onbekenden) die het rekenwerk gelijkmatig over de processoren verdeelt. 1.5 Classificatie van rekenproblemen De keuze tussen verschillende decompositie-strategiën en programmeerparadigmas wordt sterk bepaald door een paar algemene karakteristieken van het probleem dat moet worden opgelost. Volgens (Fox, 1994) zijn hierbij vooral de ruimtelijke en temporele structuur van het algoritme van belang en moeten er vijf categoriën worden onderscheiden: Versie 1.0, oktober

13 Natuurlijk parallelle algoritmes (embarrassingly parallel) algoritmes die bestaan uit een groot aantal onafhankelijke berekeningen. Bijvoorbeeld het evalueren van zetten in een schaakprogramma. Synchrone en los-synchrone algoritmes dit zijn algoritmes waarin steeds gelijktijdige updates worden gedaan aan een groot aantal data-items. Het onderscheid tussen synchroon en los-synchroon is of de updates van alle data-items identiek zijn of niet, en bepaalt of een algoritme op een SIMD parallelle computer kan worden uitgevoerd. Dit is is tegenwoordig minder belangrijk. Los-synchrone problemen kunnen goed met een data parallelle benadering worden aangepakt. Veel tijdsintegratie-procedures en iteratieve solvers vallen in deze klasse. A-synchrone algoritmes dit zijn algoritmes met verschillende data-items (verschillende updateberekeningen) en met in de tijd variërende interactiepatronen. Voorbeelden hiervan zijn discrete-event simulaties met een groot aantal verschillende actoren en parallelle directe solvers voor ijle matrices. Samengestelde problemen dit zijn algoritmes die zijn opgebouwd als samenstelling van meerdere verschillende algoritmes die ieder voor zich in een van de bovenstaande categoriën kunnen vallen. Denk bijvoorbeeld aan een gekoppelde simulatie van waterbeweging en sedimenttransport. 1.6 Analyseren van parallellisme en dataafhankelijkheden Nu dat we weten waar we naartoe willen, wat er mogelijk is voor het implementeren van parallelle algoritmes, gaan we tenslotte kijken naar het parallelliseren van een algoritme zelf. In de praktijk zal dit juist een van de eerste stappen zijn, is de toepassing belangrijker dan de computer waarmee wordt gewerkt, maar bovenstaande uitleg is een nuttige achtergrond voor het huidige onderwerp. Het probleem van het parallelliseren van een algoritme is dat je niet zomaar opeenvolgende berekeningen tegelijkertijd mag uitvoeren: als de volgende berekening het resultaat van een vorige gebruikt dan is er een data-afhankelijkheid die ook bij parallelle verwerking gerespecteerd moet worden, zoals bijvoorbeeld in het volgende code fragment: nfac = 1 do 100 i=2,n nfac = nfac * i 100 continue Versie 1.0, oktober

14 De data-afhankelijkheden van een algoritme kunnen worden weergegeven in een gerichte graaf, de dataafhankelijkheidsgraaf. De knopen in de graaf zijn de berekeningen van het algoritme, de pijlen geven een informatie-stroom weer: van de berekening die een resultaat produceert naar de berekening die dit resultaat gebruikt. De data-afhankelijkheidsgraaf geeft al het parallellisme weer dat er bestaat bij het gekozen niveau van de te onderscheiden berekeningen. Die berekeningen kunnen zo klein zijn als een enkele optelling, maar kunnen ook wat groter gekozen worden zoals bijvoorbeeld de sommatie van de elementen van een vector, de berekening van een inprodukt e.d. Wanneer de berekeningen verder worden samengevoegd tot grotere eenheden dan wordt de graaf ook wel een taakgraaf genoemd, zie paragraaf 1.4. Twee berekeningen in een dataafhankelijkheidsgraaf of twee taken in een taakgraaf kunnen tegelijkertijd worden uitgevoerd als er geen pad bestaat in de graaf van een van beiden naar de andere. We bekijken als voorbeeld verschillende iteratieve oplosmethoden voor voor de Poisson-vergelijking u u f op een vierkant domein [0,1] [0,1] met xx yy (Dirichlet) randvoorwaarde u 0. Bij gebruik van centrale differenties en een constante roosterafstand h 1 ( n 2) levert dit het bekende stelsel vergelijkingen op: u u 4u u u h f (1.1) 2 i, j 1 i 1, j i, j i 1, j i, j 1 i, j De resulterende matrix is ijl, heeft vijf niet-nul elementen per rij en heeft bandbreedte 2n 1. De matrix is positief definiet (alle eigenwaarden groter dan nul) en net aan diagonaal dominant: eigenschappen die van belang zijn voor de convergentie-snelheid van iteratieve solvers. De eerste solver die we bekijken is de Jacobi-methode. In iedere iteratie q 1, 2, wordt de volgende berekening uitgevoerd: for j 1 to n for i 1 to n u ( h f u u u u ) / 4 q 2 q 1 q 1 q 1 q 1 i, j i, j i, j 1 i 1, j i 1, j i, j 1 (1.2) Het is duidelijk dat alle berekeningen binnen een iteratie volledig onafhankelijk van elkaar kunnen worden uitgevoerd. Maar ook kunnen berekeningen van verschillende iteraties tot op zekere hoogte tegelijkertijd worden uitgevoerd. q Bijvoorbeeld kan worden nagegaan dat de berekening van u i, j niet afhankelijk is, ook niet indirect, van de berekening van q s ui, j, voor s 1. Versie 1.0, oktober

15 Met de data-afhankelijkheidsgraaf kunnen in principe allerlei analyses worden uitgevoerd. Bijvoorbeeld kan de lengte van het langste pad worden bepaald. Hiervoor wordt aan iedere berekening bepaalde kosten toegevoegd, en de kosten van het langste pad geven dan de kleinst mogelijke rekentijd bij parallelle verwerking. Daarbij wordt er overigens wel vanuit gegaan dat er een onbeperkt aantal processoren beschikbaar is en dat communicatie oneindig snel verloopt. Verder kunnen er verschillende toewijzingen van de berekeningen aan abstracte processoren worden gemaakt en op dezelfde manier worden beoordeeld. Op verschillende manieren kan hiermee invulling worden gegeven aan de hoeveelheid parallellisme in het algoritme (maximale breedte, gemiddelde breedte, langste pad t.o.v. totale hoeveelheid werk, e.d.). Een relevant getal voor de Jacobi-methode is de breedte: er kunnen steeds n n processoren worden benut. Een tweede iteratie-methode die kan worden gebruikt voor het Poisson-probleem is de Gauss-Seidel methode: for j 1 to n for i 1 to n u ( h f u u u u ) / 4 q 2 q q q 1 q 1 i, j i, j i, j 1 i 1, j i 1, j i, j 1 (1.3) Deze versie van Gauss-Seidel gebruikt de lexicografische nummering waarbij, in een sequentiële implementatie, van links naar rechts alle punten binnen een rij, en daaromheen van onder naar boven alle rijen een voor een worden afgelopen. Steeds worden zo veel mogelijk waardes op het nieuwe iteratieniveau q gebruikt, n.l. voor de punten links en onder het punt waarvoor de berekening wordt uitgevoerd. Als er alleen binnen een iteratie wordt geparallelliseerd, bijvoorbeeld omdat er na iedere iteratie op basis van alle nieuwe waardes wordt bekeken of er wel of geen volgende iteratie nodig is, dan is de lengte van het langste pad in de data-afhankelijkheidsgraaf 2n 1 stappen, en zijn er n processoren nodig om deze minimale executietijd te bereiken. Een mogelijke mapping en scheduling van de berekeningen waarmee dit wordt bereikt is rijgewijze verdeling van de roosterpunten over de processoren, en gelijktijdige q berekening van de waardes u i, j voor i j s (diagonalen van het rekenrooster). Een belangrijke eigenschap van een opdeling van de graaf in stukken is de granulariteit (omvang) van de stukken. Iedere afhankelijkheid tussen berekeningen van verschillende processoren vereist communicatie en/of synchronisatie, en hieraan is steeds een systeemafhankelijke opstarttijd verbonden. Een fijnkorrelige opdeling van een algoritme kan hierdoor alleen op een computer met een kleine opstarttijd efficient worden uitgevoerd, een grofkorrelige opdeling is minder kritisch ten aanzien van de gebruikte computer. Deze Versie 1.0, oktober

16 granulariteit verschilt sterk tussen de Jacobi en Gauss-Seidel methodes: bij gebruik van n processoren kunnen deze bij de Jacobi-methode steeds n roosterpunten achter elkaar updaten, in de Gauss-Seidel methode moet er (bij n processoren) na iedere update-stap afzonderlijk worden gecommuniceerd. Bovenstaande laat zien dat de data-afhankelijkheidsgraaf een nuttig hulpmiddel kan zijn bij het bepalen van het potentieel parallellisme in een algoritme en bij het zoeken van geschikte manieren om dit parallellisme kan worden uitgebuit. Het nut van deze methode moet overigens niet worden overschat: in de praktijk blijkt er vooral op relatief kleine aantallen processoren te worden gewerkt, en de communicatietijd wordt in de analyse grotendeels genegeerd. Verder is er hierboven geen aandacht besteed aan de numerieke efficiëntie van de verschillende algoritmen, terwijl de verschillen hierin enorm groot kunnen zijn. Zo zijn er met de Gauss-Seidel methode voor het Poissonprobleem half zoveel iteraties nodig als met de Jacobi-methode, en kan er met over-relaxatie in de Gauss-Seidel methode nog een enorme winst worden behaald. Dit is snel belangrijker de verschillen in speedup, de efficiëntie van een parallelle implementatie, zeker wanneer er niet al te veel processoren worden gebruikt. In de praktijk van parallel WAQUA/TRIWAQ wordt dataafhankelijkheidsanalyse vooral op de volgende manier gebruikt. Aan de hand van deze analyse kan worden bepaald wat er moet worden gecommuniceerd tussen de verschillende processoren. Dit wordt geïllustreerd aan de hand van de redblack Gauss-Seidel methode voor het Poisson-probleem. for j 1 to n for i 2 to n step 2 (odd( i j)) u ( h f u u u u ) / 4 for j 1 to n q 2 q 1 q 1 q 1 q 1 i, j i, j i, j 1 i 1, j i 1, j i, j 1 for i 1 to n step 2 (even( i j)) u ( h f u u u )/4 1, u, 1 q 2 q q q q i, j i, j i, j 1 i 1, j i j i j (1.4) Hoe deze methode is afgeleid (aangepaste nummering van roosterpunten volgens schaakbord-patroon, matrix-splitting L D vs. U) of hoe de convergentie zich verhoudt tot die van de Gauss-Seidel methode met lexicografische nummering (asymptotisch hetzelfde als de matrix symmetrisch positief definiet is) is voor de volgende analyse niet van belang. Belangrijk is om te herkennen dat alle berekeningen in de eerste loop onafhankelijk van elkaar zijn, evenals alle berekeningen in de tweede loop. De tweede loop gebruikt echter wel de resultaten van de eerste loop, en de eerste loop gebruikt resultaten van de tweede loop uit de voorgaande iteratie (q-1). Versie 1.0, oktober

17 Een geschikte manier om bovenstaande berekening te parallelliseren binnen het process/channel-paradigma (expliciet communicatie aangeven) is dus om een statische roosteropdeling te maken in het gewenste aantal deelroosters, om per processor de voor het eigen deelrooster benodigde f i, j en u te bepalen, en om dan voorafgaand aan iedere loop een 0 i, j communicatie-stap in te lassen om de benodigde waardes van buurpunten te verkrijgen. Welke waardes zijn dit? Het zijn steeds de nieuwste iteranden voor de roosterpunten buiten het eigen deelrooster. Welke roosterpunten buiten het eigen deelrooster? Dit is analytisch uit te schrijven indien we rechthoekige deelroosters veronderstellen. Maar dat is een zware beperking voor WAQUA/TRIWAQ, omdat er veel landpunten zijn waarvoor geen berekeningen hoeven te worden gedaan. Daarom gebruiken we de volgende techniek. Initialiseer een array met 1-en in de roosterpunten van het eigen deelrooster, en met 0-en daarbuiten. Initialiseer een tweede array op 0. Voer een loop uit over alle ( i, j ) van het eigen deelrooster waarvoor i joneven is, en markeer daarvoor steeds de vier naburige punten met 1 in het tweede array. De punten waarvoor er moet worden gecommuniceerd voorafgaand aan de eerste van de twee loops van de red-black Gauss-Seidel methode zijn nu die punten waarvoor het tweede array een 1 bevat en het eerste array een 0 bevat: nodig in de berekening èn niet van het eigen deelrooster. Dit is het principe van data-analyse met behulp van index-sets en stencils, wat verder wordt uitgewerkt in hoofdstuk 3 van deze syllabus. Tenslotte wordt opgemerkt dat afhankelijkheidsanalyse ook een belangrijk hulpmiddel is voor het doorgronden van stukken programma-code. Een verschil ten opzichte van het analyseren van algoritmes is dat in een algoritme een waarde x q i steeds uniek is gedefiniëerd, terwijl variabelen in een programma steeds nieuwe waardes kunnen krijgen. Hierdoor zijn er meerdere soorten afhankelijkheden van belang: Data-afhankelijkheid: een berekening gebruikt het resultaat van een vorige berekening. Eerstgenoemde berekening mag de waarde pas lezen als de ander klaar is met schrijven. Anti-afhankelijkheid: een berekening overschrijft de waarde van een variabele die nog nodig is voor een andere berekening. De overschrijvende berekening mag pas worden uitgevoerd als de andere berekening klaar is met lezen. Output-afhankelijkheid: twee berekeningen wijzigen dezelfde variabele. De berekeningen moeten in de goede volgorde worden uitgevoerd om hetzelfde eindresultaat op te leveren. Versie 1.0, oktober

18 In een process/channel programmeermodel moet de programmeur alleen de eerste soort afhankelijkheden in het programma detecteren. In een shared data programmeermodel zijn ook de andere afhankelijkheden van belang. Indien deze niet goed worden onderkend dan kunnen er race-condities ontstaan: het resultaat van het programma is afhankelijk van de precieze timing en snelheid van de verschillende rekenprocessen. Versie 1.0, oktober

19 2 Globaal ontwerp van parallel WAQUA/TRIWAQ In het vorige hoofdstuk is een introductie gegeven in de verschillende aspecten die er komen kijken bij parallel programmeren, en zijn er per onderdeel verschillende benaderingen gepresenteerd. Nu beschrijven we hoe die theorie is toegepast voor parallel WAQUA/TRIWAQ. Enerzijds geeft dit een globaal beeld van hoe parallel WAQUA/TRIWAQ in elkaar zit, anderzijds is het een voorbeeld van hoe men te werk kan gaan bij parallellisatie van een willekeurig ander probleem. 2.1 Uitgangspunten bij de parallellisatie Het globaal ontwerp van de huidige versie van parallel WAQUA/TRIWAQ is grotendeels gemaakt in Er was toen ervaring met een prototype parallelle versie van de eerste versie van TRIWAQ met vaste lagen, en de eerste versie van WAQUA/TRIWAQ in SIMONA was zojuist gereed gekomen. Er was nog weinig zicht op de huidige convergentie van parallelle computer-architecturen, en daarom was overdraagbaarheid (portabiliteit) een belangrijk doel. Netwerken van werkstations waren sterk in opmars, en zouden als het eventueel mogelijk was moeten kunnen worden gebruikt. De eerste optie die is onderzocht en is afgeschoten is die van automatische parallellisatie met behulp van een parallelliserende compiler. Automatische parallellisatie lijkt niet principiëel onmogelijk, maar er is wel een echte SMP machine nodig (geen NUMA multiprocessor) om een goede speedup te behalen op meer dan een handvol processoren. Bovendien is er een heel goede compiler nodig om het parallellisme te herkennen en het werk slim te verdelen of moet de code fors worden geherstructureerd. Herstructurering was sterk ongewenst vanwege de omvang van de programmatuur en daaraan verbonden onderhoudskosten, ook de gevectoriseerde versie van TRIWAQ is zeer moeilijk up-to-date te houden geweest met de standaardversie. De twee grootste problemen voor automatische parallellisatie zijn de double sweeps voor de tridiagonale stelsels voor waterstanden (weinig parallellisme, steeds verschillende verdelingen van roosterpunten over de processoren), en alle verschillende soorten arrays en loops voor verschillende soorten punten uit het grid (randpunten, barriers, source-punten e.d.). De handmatige parallellisatie van WAQUA/TRIWAQ wordt beschreven aan de hand van de vijf onderwerpen die in paragraaf 1.1 zijn onderscheiden. Versie 1.0, oktober

20 2.1.1 Bepalen van het parallellisme Als eerste is natuurlijk gekeken naar de numerieke methoden die in de nieuwe versie van TRIWAQ in SIMONA werden gebruikt. Deze leken sterk op die van de vaste-lagen versie van TRIWAQ, behalve dat het aantal lagen per roosterpunt in het horizontale vlak constant is, en dat de voor parallellisatie erg vervelende CUE -methode van de eerste versie van TRIWAQ in de nieuwe versie was vervangen door de red-black Jacobi methode Classificeren soort parallellisme De gebruikte algoritmen kunnen worden geclassificeerd als lossynchroon. Veel berekeningen in TRIWAQ zijn van de vorm voor alle roosterpunten doe.... Eerst worden voor alle roosterpunten de coëfficiënten van de op te lossen stelsels vergelijkingen bepaald, daarna wordt voor alle roosterpunten een iteratie uitgevoerd, enz. De berekeningen verschillen tussen verschillende roosterpunten, bijvoorbeeld in de gebruikte differentie-benadering voor de advectieve termen, of t.g.v. aanpassingen aan de discretisaties nabij open/gesloten randen. Een aantal kernels kan ook als a-synchroon worden getypeerd: de double-sweep solver voor tri-diagonale stelsels, en de Gauss-Seidel gebaseerde solvers voor de turbulente grootheden en voor de impulsvergelijking in WAQUA. De oorspronkelijke implementatie van WAQUA is ook meer a- synchroon dan de berekeningen van TRIWAQ. De verschillende stadia van de berekeningen waren sterk met elkaar vervlochten (bijv. opstellen/oplossen vergelijkingen). Om parallellisatie met de hieronder beschreven aanpak mogelijk te maken is de implementatie geherstructureerd, zodanig dat deze grote overeenkomsten heeft met die van TRIWAQ Parallelle computer-architecturen Zoals hierboven al is aangegeven was portabiliteit een belangrijke doelstelling voor de parallellisatie. Verder waren er clusters van werkstations beschikbaar bij Rijkswaterstaat en bij de TU Delft, en werden deze als een relatief goedkope en krachtige rekenfaciliteit gezien. Daarom werd ervoor gekozen om de parallelle versie geschikt te maken voor multi-computers (zonder shared memory), tenzij dat echt door performance of complexiteit onmogelijk zou zijn Parallel programmeerparadigma Vanwege de wens om netwerken van werkstations te kunnen gebruiken en vanwege de onmogelijkheid van automatische parallellisatie lag de keuze voor de een of andere variant van Versie 1.0, oktober

21 process/channel paradigma voor de hand. Ieder rekenproces krijgt zijn eigen subdomein toegewezen, en doet daar alle berekeningen voor. De processen communiceren met elkaar ten aanzien van de waardes van roosterfuncties nabij subdomeinranden. Daarbij wordt niet gezegd of die communicatie via message passing gebeurt of via bufferarrays in een shared memory. Een ander aspect van het programmeerparadigma betreft het opstarten van de rekenprocessen en verdelen van de data van het probleem. Hierbij is ervoor gekozen om ieder rekenproces zijn eigen versie van de SIMONA-omgeving te geven (geheugenadministratie). Dit om te voorkomen dat er een compleet gedistribueerde implementatie van die omgeving moet worden gemaakt voor bepaalde parallelle computers. Daarom is de gevolgde aanpak goed te karakteriseren als het koppelen van verschillende instanties van het oorspronkelijke programma Probleemdecompositie Binnen de beschreven aanpak zijn er nog verschillende mechanismen mogelijk voor het verdelen van het rekenwerk. Er is gekozen voor een aanpak waarbij het rekenrooster wordt verdeeld in deelroosters, en waarbij een rekenproces volledig verantwoordelijk is voor de berekeningen van zijn deelrooster. Dit omdat er weinig verschillende functies in het programma zijn die onafhankelijk van elkaar, gelijktijdig kunnen worden uitgevoerd. De roosteropdeling wordt alleen bepaald voor het horizontale vlak omdat het aantal lagen in TRIWAQ-simulaties relatief klein is en omdat een aantal berekeningen in vertikale richting sterk is gekoppeld. De opdeling wordt eenmalig bepaald en is dan vast gedurende de hele berekening (statische i.p.v. dynamische roosteropdeling). Het dynamisch kunnen variëren van de roosteropdeling zou voordelig kunnen zijn in situaties waarin veel droogvallen en onderlopen optreedt (bijv. hoogwatergolven in rivieren), maar is moeilijk te implementeren binnen de gekozen strategie van het koppelen van instanties van het oorspronkelijke programma. Twee manieren om ook bij veel droogvallen en onderlopen een redelijke load-balance te handhaven zijn om bij het verdelen rekening te houden met de bodemligging, en om meer subdomeinen/rekenprocessen te gebruiken dan er processoren zijn. 2.2 Overzicht van de systeem-structuur Het ontwerp dat gaandeweg de parallellisatie tot stand is gekomen wordt gepresenteerd in Figuur 1. Versie 1.0, oktober

22 Title: opratovw4.eps Creator: fig2dev Version 3.2 Patchlevel 3c Preview: This EPS picture was not saved with a preview included in it. Comment: This EPS picture will print to a PostScript printer, but not to other types of printers. Figuur 1 Globale systeem-structuur van parallel WAQUA/TRIWAQ Een aantal belangrijke aspecten van dit ontwerp zijn als volgt. 1) Het externe gedrag van de parallelle versie is hetzelfde als van de sequentiële versie, in die zin dat een enkele globale invoer (SDS-)file wordt geconverteerd in een enkele globale uitvoerfile. Hierdoor kan parallel rekenen worden gebruikt in combinatie met alle standaard pre- en postprocessing programma s, en zijn de details van de parallellisatie grotendeels voor de eindgebruiker. Twee aspecten waarvoor dit niet geldt zijn de message-output en de report-file (per subdomein apart). 2) De partitioner is geïntroduceerd om het rekenrooster te verdelen in min of meer evengrote stukken, en om de gegevens van de initiële globale SDS-file te distribueren over de deeldomeinen. Hij zorgt ervoor dat ieder rekenproces een SDS-file krijgt toegewijzen die vrijwel volledig aan het format van sequentiëel WAQUA/TRIWAQ voldoet. De collector doet het tegenovergestelde, voegt de uitvoerdata per deeldomein samen tot een globale uitvoerfile. Het distribueren en collecteren van de gegevens is strikt gescheiden van het rekenen door de rekenprocessen. Dit zorgt ervoor dat er binnen een rekenproces geen verwarring kan ontstaan Versie 1.0, oktober

23 tussen de globale en lokale dimensies van allerlei soorten arrays. 3) Het master-proces (executive) heeft slechts een zeer beperkte functie. Hij zorgt voor het opstarten van de rekenprocessen, voor het verzamelen van de diagnostische uitvoer in een enkele file, en voor het signaleren en afhandelen van problemen met de rekenprocessen. 4) De WAQUA/TRIWAQ rekenprocessen zijn ieder apart instantiaties van het volledige programma, inclusief file I/O, initialisaties e.d. Het gezichtspunt dat hierbij wordt gebruikt is dat van het koppelen van verschillende simulaties, in plaats van dat een enkele simulatie wordt opgesplitst. Ten behoeve van deze koppeling is het oorspronkelijke programma uitgebreid, onder andere met een nieuw type randvoorwaarde en met communicatie van waardes bij subdomeinranden. Verder wordt de oorspronkelijke programma-code volledig hergebruikt. Het gezichtspunt van gekoppelde zelfstandige simulaties wordt in praktijk gebruikt voor het realiseren van domein decompositie en voor on-line koppelingen. Voor zulke koppelingen is het nuttig als de rekenprocessen zo min mogelijk aannames maken over de omgeving waarbinnen ze draaien. Mede hierom hebben de rekenprocessen in een parallelle run slechts beperkte informatie over de overall-structuur van de berekening. 5) De communicatie tussen de verschillende rekenprocessen is strikt gescheiden van de berekeningen in het programma. Aan de ene kant is dit gedaan vanwege portabiliteit, opdat het precieze communicatiesysteem van de parallelle computer kan veranderen zonder grote gevolgen voor het parallelle programma. Een belangrijkere reden voor het introduceren van een aparte communicatie-bibliotheek is echter het verstoppen van details voor applicatie-programmeurs. Bijvoorbeeld de boekhouding van de precieze vorm van subdomein-randen en buurprocessen voor applicatieprogrammeurs. Met de communicatie-bibliotheek wordt volledige vrijheid bij het opdelen van grillige rekenroosters geïmplementeerd. 2.3 Uitwerking van de rooster-opdeling In een parallelle simulatie wordt ieder deeldomein door een apart rekenproces afgehandeld alsof het een zelfstandig domein is. Hiervoor is het oorspronkelijke programma uitgebreid om te kunnen samenwerken met andere instanties van zichzelf. Versie 1.0, oktober

24 Het basis-principe voor parallellisatie van de berekeningen van WAQUA/TRIWAQ is data-parallellisme. Ieder rekenproces voert in principe dezelfde reeks van stappen uit (de gezamelijke agenda), maar dan alleen op het eigen gedeelte van de data. Het eigen gedeelte van de data wordt gegeven door de opdeling van het globale rooster. Deze opdeling is geïmplementeerd als een array HOWNER(1:nmax,1:mmax) waarin per roosterpunt (array-index) van het globale domein wordt aangegeven welk rekenproces hiervan de eigenaar is. De rekenprocessen werken echter allemaal op een eigen lokale geheugenruimte, en het zou zonde zijn om ze allemaal arrays met de afmetingen van het globale domein te laten bewaren. In plaats daarvan wordt er per deeldomein een uitsnede gemaakt uit het globale domein die voldoende is voor de berekeningen voor het deeldomein. Deze uitsnede van het globale domein bevat alle roosterpunten van het subdomein, plus aan alle kanten drie extra rijen en kolommen van punten van de buurdomeinen. Deze extra punten van de buurdomeinen worden gebruikt voor de opslag van gegevens van de buurdomeinen die nodig zijn in de berekeningen, zoals waterstanden en zout-concentraties, en worden de guard band van het deeldomein genoemd. De guardbandbreedte is instelbaar, en staat standaard op 3 ingesteld ten behoeve van het stoftransport-gedeelte van TRIWAQ. Beschouw als voorbeeld een globaal rooster van 10x30 punten dat stripsgewijs wordt verdeeld in drie deeldomeinen. Stel dat deeldomein 1 loopt van n=1 tot en met 12, deeldomein 2 loopt van n=13 t/m 18 en dat deeldomein 3 loopt van n=19 tot en met 30. Dan worden de uitsnedes van het globale domein respectievelijk[1..10] [1..15], [1..10] [10..21] en [1..10] [15..30]. Merk op dat er in parallelle berekeningen geen guard band wordt toegevoegd buiten het oorspronkelijke globale domein, dit is anders bij gebruik van domein decompositie met horizontale verfijning. Vervolgens worden de uitsnedes per deeldomein verschoven zodanig dat het eerste punt uitkomt op (1,1). Dit is nodig omdat de ondergrens van allerlei arrays in WAQUA/TRIWAQ is vastgelegd op 1. Tenslotte worden de afmetingen mmax en nmax voor de subdomeinen berekend. In het gegeven voorbeeld zijn de verschuivingen van de drie deeldomeinen de vectoren (0,0), (0,9) en (0,14), en zijn de afmetingen nmax respectievelijk 15, 12 en 21. Deze getallen worden berekend door de partitioner COPPRE bij het aanmaken van de SDS-files per deeldomein. Op deze SDS-files worden ook de bijbehorende uitsnedes van het HOWNER-array gezet: per SDS-file is dit een array POWNER(1:nmax,1:mmax), met de voor dat subdomein bepaalde afmetingen nmax en mmax. Versie 1.0, oktober

25 2.4 Implementatie van numerieke berekeningen ( Loops ) in WAQUA/TRIWAQ De berekeningen in WAQUA/TRIWAQ bestaan grotendeels uit loops over alle roosterpunten. Bijvoorbeeld een loop voor het berekenen van de horizontale termen in de impuls-vergelijking, een loop voor de dichtheidsterm, enz. Deze data-parallelle stappen worden in WAQUA/TRIWAQ op een specifieke manier geïmplementeerd, namelijk via de irogeo-tabel. Deze implementatie wordt toegelicht voordat we de parallellisatie van zulke berekeningen bespreken aan de hand van de gekozen roosteropdeling. De irogeo-tabel geeft een beschrijving van de rijen en kolommen van het rekenrooster. Het rekenrooster is oorspronkelijk als volgt tot stand gekomen: 1) De gebruiker specificeert de afmetingen mmax en nmax van de rechthoek waarbinnen het rooster ligt. 2) De gebruiker geeft de grid-enclosures op. Dit zijn polygonen met horizontale, vertikale of diagonale lijnstukken die het actieve gedeelte van het rooster omsluiten of juist eilanden eruit wegsnijden. De enclosure-punten zelf horen niet tot het echte rekenrooster. Alle punten (m,n) die op deze manier worden geselecteerd worden cellen van het rekenrooster. Op alle hoekpunten van de cellen worden kromlijnige coördinaten en dieptecijfers opgegeven, de hoekpunten zijn de diepte-punten van een WAQUA-rooster. In de middens van alle zijvlakken van de cellen worden snelheidspunten gedefinieerd, en de middens van de cellen worden waterstandspunten genoemd. 3) De gebruiker kan op segmenten van de grid-enclosures openingen definieren. Ten behoeve van de randafhandeling wordt er een ring van extra rand-cellen rondom de echte cellen van het rekenrooster gelegd. Verschillende stukken van deze ring mogen niet met elkaar samenvallen; eilanden moeten tenminste twee cellen breed zijn, en er mogen geen scherpe hoeken in enclosures zitten. 4) De programmatuur bepaalt voor welke roosterpunten (m,n) er echte cellen zijn gedefiniëerd en waar er randcellen bestaan. Dit wordt bijvoorbeeld opgeslagen in het kenmerk-array KCS: de codes 0, 1 en 2 worden gebruikt om aan te geven of een punt een landpunt is, of dat het een intern punt of een open randpunt is. Voor de interne punten (KCS=1) wordt ook de irogeo-tabel opgesteld. Figuur 2 geeft een voorbeeld van een irogeo-tabel. Versie 1.0, oktober