Tor-Eirik Bakke Lunde

Like dokumenter
Tor-Eirik Bakke Lunde

UNIVERSITETET I OSLO

Del 4 Noen spesielle C-elementer

MPIntroduksjon Et eksempel

TDT4102 Prosedyre og Objektorientert programmering Vår 2014

Repetisjon: Statiske språk uten rekursive metoder (C1 og C2) Dagens tema Kjøresystemer (Ghezzi&Jazayeri 2.6, 2.7)

Dagens tema Kjøresystemer (Ghezzi&Jazayeri 2.6, 2.7)

Programmeringsspråket C Del 3. Hans Petter Taugbøl Kragset

TDT4105 IT Grunnkurs Høst 2014

INF1020 Algoritmer og datastrukturer GRAFER

Eksamen i INF3380 våren 2017

UNIVERSITETET I OSLO

TDT4110 Informasjonsteknologi grunnkurs: Programmering: En større case. Professor Alf Inge Wang

Læringsmål og pensum. En større case. Mål Lære å lage større og sammensatte programmer Pensum Kapitlene 1-9 og 12.

Mål. Pensum. TDT4110 Informasjonsteknologi grunnkurs: Tema: Et større case. Terje Rydland - IDI/NTNU. Lære å lage større og sammensatte programmer

UNIVERSITETET I OSLO

Installere JBuilder Foundation i Mandrake Linux 10.0

Tor-Eirik Bakke Lunde

Eksamen i INF3380 våren 2018

PG 4200 Algoritmer og datastrukturer Innlevering 2

Oversikt over flervalgstester på Ifi

INF1000 (Uke 15) Eksamen V 04

INF1000 (Uke 15) Eksamen V 04

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000

I dag skal vi ved hjelp av ganske enkel Python-kode finne ut om det er mulig å tjene penger på å selge og kjøpe en aksje.

UNIVERSITETET I OSLO

Flerveis søketrær og B-trær

Hukommelseshierarki. 16/3 cache /3 virtuell hukommelse in 147, våren 1999 hukommelseshierarki 1

UNIVERSITETET I OSLO

INF2220: Gruppe me 2. Mathias Lohne Høsten 2017

Her skal du lære å programmere micro:biten slik at du kan spille stein, saks, papir med den eller mot den.

Dagens plan. INF Algoritmer og datastrukturer. Koding av tegn. Huffman-koding

INF Algoritmer og datastrukturer

INF Algoritmer og datastrukturer

INF2220: Forelesning 2

KANDIDATEN MÅ SELV KONTROLLERE AT OPPGAVESETTET ER FULLSTENDIG

UNIVERSITETET I OSLO

Lineære ligningssystemer og gausseliminasjon

EKSAMENSFORSIDE SKRIFTLIG EKSAMEN

BYFE/EMFE 1000, 2012/2013. Numerikkoppgaver uke 40

Kapittel 5: Mengdelære

Programmeringsspråket C Del 2. Hans Petter Taugbøl Kragset

... Når internminnet blir for lite. Dagens plan: Løsning: Utvidbar hashing. hash(x) katalog. O modellen er ikke lenger gyldig ved

EKSAMEN. Dato: 18. mai 2017 Eksamenstid: 09:00 13:00

Del 1 En oversikt over C-programmering

Definisjon. I et binært tre har hver node enten 0, 1 eller 2 barn

Ny/utsatt EKSAMEN. Dato: 6. januar 2017 Eksamenstid: 09:00 13:00

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000 v2008

MAT Oblig 1. Halvard Sutterud. 22. september 2016

Lineære ligningssystemer og gausseliminasjon

Tirsdag 21/11. Onsdag 24/11. Tirsdag 12/12. TDT4110 Informasjonsteknologi grunnkurs: Tema: Et større case

PG4200 Algoritmer og datastrukturer Forelesning 5 Implementasjon av lister

EKSAMENSOPPGAVE. INF-1100 Innføring i programmering og datamaskiners virkemåte. Ingen. Elektronisk (WiseFlow) Robert Pettersen

Oppgave 3 a. Antagelser i oppgaveteksten. INF1020 Algoritmer og datastrukturer. Oppgave 3. Eksempelgraf

Obligatorisk oppgave 1: Regneklynge

Kanter, kanter, mange mangekanter. Introduksjon: Steg 1: Enkle firkanter. Sjekkliste. Skrevet av: Sigmund Hansen

INF2220: Forelesning 2. Balanserte søketrær Rød-svarte trær (kapittel12.2) B-trær (kapittel 4.7)

Kodegenerering del 3: Tilleggsnotat fra AHU Samt litt om class-filer og byte-kode INF5110 V2007. Stein Krogdahl, Ifi UiO

Et eksempel: Åtterspillet

NOTAT (pensum!) Javas klasse-filer, byte-kode og utførelse. INF 5110, 10/5-2011, Stein Krogdahl

TDT4110 Informasjonsteknologi grunnkurs: Kapittel 7 Filer og unntak ( exceptions ) Professor Alf Inge Wang Stipendiat Lars Bungum

Hvilken BitBot går raskest gjennom labyrinten?

INF2220: Forelesning 2

Datatyper og typesjekking

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000 h2006

public static <returtype> navn_til_prosedyre(<parameter liste>) { // implementasjon av prosedyren

Programmeringsspråket C Del 3

Operasjoner på lenkede lister (enkeltlenket) Eksempel på en lenket liste: personliste. INF januar 2010 (uke 3) 2

Litt om Javas class-filer og byte-kode

Informasjon Prøveeksamen i IN1000 høsten 2018

Løsnings forslag i java In115, Våren 1996

Programmeringsspråket C Del 3

Algoritmer og datastrukturer Kapittel 11 - Delkapittel 11.2

public static <returtype> navn_til_prosedyre(<parameter liste>) { // implementasjon av prosedyren

Definisjon av binært søketre

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000 v2009

UNIVERSITETET I OSLO

OPPGAVE 1 OBLIGATORISKE OPPGAVER (OBLIG 1) (1) Uten å selv implementere og kjøre koden under, hva skriver koden ut til konsollen?

Programmeringsspråket C Del 3

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000

Programmeringsspråket C Del 3

Stack. En enkel, lineær datastruktur

MAT1030 Forelesning 25

TDT4110 Informasjonsteknologi grunnkurs: Python: Repetisjon. Professor Alf Inge Wang

Algoritmeanalyse. (og litt om datastrukturer)

INF Algoritmer og datastrukturer. Hva er INF2220? Algoritmer og datastrukturer

Plan for dagen. Vprg 4. Dagens tema - filbehandling! Strømmer. Klassen FilLeser.java. Tekstfiler

UNIVERSITETET I OSLO

INF3030, Uke 3, våren 2019 Regler for parallelle programmer, mer om cache og Matrise-multiplikasjon. Arne Maus / Eric Jul PSE, Inst.

Pekere og referanser.

MAT1030 Diskret matematikk. Mengder. Mengder. Forelesning 9: Mengdelære. Dag Normann OVER TIL KAPITTEL februar 2008

Løpende strekmann Erfaren Videregående Python PDF

Datatyper og typesjekking

MAT1030 Diskret matematikk

Sist forelesning snakket vi i hovedsak om trær med rot, og om praktisk bruk av slike. rot. barn

Datatyper og typesjekking

Kom i gang med micro:bit

Lars Vidar Magnusson

Obligatorisk oppgave 1 INF1020 h2005

Oppgave 2: Gå til roten (/) av systemet. Finn minst tre forskjellige måter å gå tilbake til hjemmekatalogen din på.

Transkript:

Obligatorisk oppgave 1 INF-3201 < Parallellprogrammering> 13. oktober 2003 Tor-Eirik Bakke Lunde torebl@stud.cs.uit.no

0: Analyse av sekvensiell kode: Identifiser og beskriv datastrukturer: Den sekvensielle nbody-koden benytter seg av to datastrukturer for å lagre data. Hvorav strukturen coords definerer to double verdier for å angi enheter fordelt i henholdsvis x og y retning, og strukturen body_s utgjør lagringstypen som blir bruk for å danne den enkeltlinkede listen som benyttes. I seg selv er disse fullt brukbare i sekvensiell kode, men for å parallellisere koden må bruken av disse elimineres eventuelt overføres til noe mer brukbart (fra mpi bibliotekets standpunkt). Dette rett og slett fordi lenkede lister per definisjon har hvert element spredd utover det fysiske minnet, og det ville vært betraktelig enklere dersom det lå sekvensielt og en kunne håndtere en enkelt blokk data, istedenfor x antall små. For å oppsummere hovedsaklige datastrukturer: coords datastruktur body_s datastruktur, sammensatt av coords og 2stk pointere. Enkeltlenket list, bestående av body_s for organisering av data. Størrelse varierende etter inndata. Identifiser dataavhengigheter: Ved å studere metoden calculate_forces er det lett å se at algoritmen ikke har noen hukommelse, dvs at hvert resultatene fra hvert tidssteg ikke er direkte avhengige av hverandre utover å danne grunnlaget for neste kalkulasjoner (F.eks Leapfrog algoritmen der enkelte verdier kalkuleres vekselvis). Utdrag: if (i == j) continue;... b[i]->f.x = b[i]->f.x + magnitude * direction.x / distance; b[j]->f.x = b[j]->f.x - magnitude * direction.x / distance; b[i]->f.y = b[i]->f.y + magnitude * direction.y / distance; b[j]->f.y = b[j]->f.y - magnitude * direction.y / distance; Ved å se på utdraget fra calculate_forces ovenfra kan en se at algoritmen for hver gjennomgang av påvirker verdiene til ikke bare element i, men også element j. Dette er en teknikk brukt for å øke ytelsen til denne sekvensielle algoritmen ved å utnytte at element i påvirker en kraft f ij på element j, og dermed også at element j påvirker i med en kraft f ji. Forholdet mellom disse to kreftene er ganske enkelt: f ij = - f ji Dette kunne blitt utnyttet bedre og spart ytterligere kalkulasjoner. If setningen inkludert øverst er tatt med ettersom at det ble antydet at denne var feil, men ved studie av kode kan man se at dersom en beregner avstanden mellom element i og j, når i==j vil en finne at de da er samme element og avstanden mellom dem blir følgelig null. De neste linjene i utdraget ville da endt opp som division by zero og resultatet lagret ville følgelig være nan (Not a number). Oppsummering: Resultater fra en gjennomgang av kode danner datagrunnlaget for neste. Utregninger utført påvirker både element j og i.

Finn Big-O for algoritmene: I den stand den sekvensielle algoritmen ble utlevert kan man observere følgende: for (i = 0; i < N; i++) { for (j = 0; j < N; j++) {... } } Utfra dette er det lett å konkludere med at algoritmen i O-notasjon tilsvarer O(n 2 + n), der + n vil være marginal ved stor N derfor står en igjen med den dominerende delen - O(n 2 ).

1: Parallellisering (Decomposition): Global synkronisering ved hver tidsperiode Data kun avhengig av data fra forrige tidsperiode (med litt modifikasjon som forklart i analysen av dataavhengigheter) Elementer kun avhengig av data fra forrige tidsperiode. Identifisering av parallelliserbare punkter: Hovedarbeidet som blir gjort i den sekvensielle koden utover rent initialisering og innlesing av data fra hard-disk blir foretatt i funksjonene calculate_forces og move_bodies, og det er her nøkkelen til en potensielt økt ytelse ligger. Funksjonene calculate_forces : O(n 2 ), dvs svært kostbar løkke og skal parallelliseres. Funksjonen move_bodies : O(n), enkel løkke som oppdaterer elementenes posisjon etter hver kalkulasjon. Selve parallelliseringen: Valgte en svært enkel løsning, med å benytte seg av en mindre effektiv, men fortsatt mye bedre parallellisert O(n 2 ) algoritme. Datastrukturer: Den sekvensielle koden benytter seg av en linket liste som er vanskelig og en stor feilkilde under utvikling av parallelle programmer, samt at vi trenger en datatype MPI forstår. Derfor definerer vi en ny datastruktur som er mer MPI-vennlig, og vises nedenfor. typedef struct { double px, py; double vx, vy; double fx, fy; double m; } Body; MPI_Datatype MPI_BODY; Ved å benytte oss av en slik struktur, bestående av homogene datatyper kan en definere en ny MPIdatatype basert på denne. Etter å ha gjort dette kan en med enkelhet utveksle data av denne typen uten frykt for feil. Fremgangsmåten for dette vises nedenfor, og er avhengig av dataen definert i forrige kodeutsnitt. MPI_Type_contiguous(7, MPI_DOUBLE, &MPI_BODY); MPI_Type_commit(&MPI_BODY); Ettersom vi allerede har funksjonalitet som leser data inn fra filer ville det vært bortkastet å prøve å spare noen få prosessorsykluser på å skrive om disse. Isteden defineres en ny pointer øverst i koden, og etter at data er lest inn fra disk allokeres en passende stor datablokk for å holde denne dataen og kopierer dem inn i det mer brukbare formatet, og måten dette blir gjort på kan observeres i siste delen av read_input funksjonene. Algoritmen: Som nevnt er dataen som brukes (lagret i minneområdet referert til av pekeren bodies ) avhengig kun av forrige tidsperiode. Dermed kan en sikkert dele opp elementene i grupper, der hver gruppe elementer

blir tildelt en prosess som skal foreta utregningene på dem. Ettersom denne implementasjonen er av en enkel type trenger en bare gjøre dette en gang, og blir da gjort under initialiseringen i starten av programmet. Den parallelle algoritmen fungerer nå slik: 1. Root prosessen broadcaster all informasjon ut til hver prosess. 2. Hver prosess foretar utregninger på sin utdelte gruppe elementer, inkludert regne ut krefter og endre posisjoner. 3. Root samler inn all data, slik at den har oppdatert informasjon om hvert element. 4. Gjenta steg 1-3 for hver tidsperiode Tildeling av arbeidsområde (Assignment): Statisk tildeling av grupper av elementer, p prosesser, rang 0 (p-1). Grupper blir dannet opprettet slik etter at root har lest inn data fra disk, og sendt ut antallet elementer (N) til resten av prosessene: mystart = (int *) malloc(size * sizeof(int)); myend = (int *) malloc(size * sizeof(int)); mystart[0] = 0; myend[0] = N / size; for (i = 1; i < size; i++) { mystart[i] = myend[i - 1]; myend[i] = mystart[i] + N / size; } myend[size - 1] = N; Prosess i blir tildelt element mystart[i] til myend[i]. God arbeidsfordeling ettersom hver prosess arbeider like mye (antallet elementer kan gi en prosess noen elementer mindre, men ingen som får en størstepart.). Dersom programmet kjøres i ett homogent miljø vil dette fungere utmerket, viss ikke vil den raskeste prosessen bli stående og vente på de tregere prosessene. Utveksling av data: Root foretar MPI_Bcast( ), og dermed kopierer all data om alle elementene ut til hver prosess. Etter å ha gjennomført sine kalkulasjoner sender hver prosess sitt resultat til root prosessen, som da setter det hele sammen til ett helhetlig resultat. Dette gjøres ved at MPI_Recv( ) kjøres i en løkke på root prosessen og tar imot data fra de andre prosessene.

2. Evaluering: Ettersom mye utveksling av data foregår innblandet i utregningene, så istedenfor da å tynge ned algoritmen med tidsutregninger har jeg valgt å ta en enkel løsning der initialisering plasseres utenfor målingen og slår sammen tid brukt på utregning og utveksling av data. Dette vil si at for å få ett bedre bilde av hva som foregår tas tiden ifra algoritmens run( ) starter og til den er ferdig. Selve tidsmekanismen som benyttes er MPI sin egen MPI_Wtime() funksjon. I den sekvensielle koden er ikke denne funksjonen tilgjengelig, så der er tiden differansen mellom to timeval strukturer som er fylt med data av ett kall til gettimeofday() funksjonen. Selve målingene foretas på økende antall bodies fordelt på forskjellige antall prosesser brukt til selve utregningene. Henholdsvis 100, 250, 500, 750 og 1000 bodies på 1 (sekvensiell kode), 2, 4 og 6 prosesser. Alle målingene er foretatt på Snowstorm clusteret, og nodene Compute-1-[0-5]. De kalkulerte tidene, vist på y aksen, viser hvor lang tid kalkulasjonene tok ved forskjellige mengder elementer, vist på y aksen. Den sekvensielle koden gjorde det rimelig akseptabelt med ett lavere antall elementer, men ved høyere antall elementer går med høy fart imot en praktisk uendelighet når det kommer til prosesseringstid. Ettersom den parallelle algoritmen er avhengig å overføre mye data blir fortjenesten med å øke antallet prosesser stadig mindre, men sammenlignet med den sekvensielle gjør selv bruken av to prosesser en betraktelig forskjell. En kan tydelig se ut fra grafen at ved å øke antallet prosesser fra 2 til 6 blir ikke prosesseringstiden redusert med 2/3, men kun litt over halvert dette illustrerer hvordan økt dataoverføring til slutt kommer til å overta som den begrensede ressursen, og ikke prosessering som var tilfellet tilfelle. Tendensen tyder på at ved bruk av mer enn ca 8-12 prosesser, vil fortjeneste bli negativ som følge av dette.