Rinnakkaistietokoneet luento 4 521475S
Rinnakkaiset ei-numeeriset algoritmit: transitiivisulkeuma (transitive closure) Oletetaan suunnattu graafi G = (V,E) ja halutaan tietää onko olemassa kahta pistettä (vertex) yhdistävää polkua Transitiivisulkeuma kuvaa pisteitä yhdistävät polut graafin reunoiksi, eli jos kahden pisteen välillä on yhteys (esim. toisten pisteiden kautta), kuvataan tämä pisteitä yhdistävällä reunalla (edge) A = [a ij ] on G:n vierusmatriisi, missä a ij = 1, jos pisteitä i ja j yhdistää reuna Halutaan laskea vierusmatriisi A* = [a* ij ], missä a* ij = 1 jos on olemassa polku pisteestä i pisteeseen j: A* on graafin G = (V,E*) vierusmatriisi, missä E* on E:n transitiivisulkeuma
Kuva 2.17: Esimerkki transitiivisulkeumasta
Warshallin algoritmi on hyvin tunnettu algoritmi A*:n laskemiseksi: for k=1 to n for i = 1 to n for j = 1 to n a k ij ak-1 ij U (ak-1 ik ak-1 kj ) end j end i end k Riippuvuusmatriisi: D = [ d d d ] 1 2 3 = 1 0 0 j 1 0 k i 1 k 0 k i j
riippuvuus d1 on vakio mutta d2 ja d3 muuttuvia, eli niissä on elementtejä jotka ovat riippuvaisia iteraatiosta jolla a k ij arvo päivitetään Analysoidaan rinnakkaisuutta: Kaikilla riippuvuusvektoreilla on k-elementti 1 ( 0), eli kaikki riippuvuudet tulevat edelliseltä k silmukan iteraatiolta Ei riippuvuuksia (i,j) tasossa, eli ei riippuvuusvektoreita joilla k- elementti on 0 kaikki operaation (i,j)-tasossa voidaan suorittaa samanaikaisesti (rinnakkain) k-koordinaatista tulee rinnakkaislaskennan aika koordinaatti ja kriittisen polun pituus on n ja kompleksisuus siis O(n) Kuva 2.18 näyttää algoritmin kriittisen polun, jossa jokainen piste sijaitsee (i,j)-tasossa eri k-iteraatiolla
Kuva 2.18: transitiivisulkeuman kriittinen polku (i,j,2) j d 1 (i,j,1) i
Jos riippuvuudet projisoidaan (i,j)-tasoon, voidaan katsoa millaisia tiedonsiirto vaatimuksia algoritmilla on, eli nähdään mitä tietoa tarvitaan ja kuinka kaukaa kussakin pisteessä kullakin k:n iteraatiolla (kts. kuva 2.19) Kuva esittää riippuvuuksia matriisin A:n elementtien välillä (i,j)- tasossa neljälle eri k:n iteraatiolle: nuolet näyttävät mistä elementeista ko. elementin päivitys on riippuvainen (riippuvuudet tulevat edelliseltä k:n iteraatiolta, mutta tämä ei näy, kun riippuvuudet on projisoitu (i,j)-tasoon) Olettaen, että rinnakkaistietokoneessa on käytössä tarvittava määrä tiedonsiirtoreittejä, voidaan kaikki (i,j)-tason (k:n iteraation) laskennat suorittaa yhden aikayksikön kuluessa
Kuva 2.19: transitiivisulkeuman datariippuvuudet projisoituna (i,j)-tasoon (n=4) 1 0 0 1 0 j k 1 i k 0
Rinnakkaislajittelu Lajittelussa (sorting) joukko arvoja järjestellään kasvavaan tai vähenevään järjestykseen Tietokoneella tehtävää lajittelua käytetään mm. kääntäjissä, tekstinkäsittelyssä, muistinhallinnassa, jne. Lajittelualgoritmit voidaan luokitella sisäisiin ja ulkoisiin: työmuistissa tapahtuva lajittelu on sisäistä ja massamuistissa tapahtuva lajittelu vastaavasti ulkoista Optimaalinen sekventiaalinen lajittelualgoritmi vaatii O(nlog n) vertailua, missä n on lajiteltavien elementtien lukumäärä Optimaalinen rinnakkaisalgoritmi, joka käyttää n kpl prosessoreita, lajittelee n elementtiä O(log n) aikayksikössä Useat rinnakkaisalgoritmit saavuttavat optimaalisen ajan, mutta käyttävät usein useampaa kuin n prosessoria Eräät ensimmäisistä nopeista rinnakkaisalgoritmeista hyödyntävät verkkolajittelua (network sorting), missä usean prosessorin muodostama verkko suorittaa vertailuja ja arvojen vaihtoja rinnakkain
Pariton-parillinen lajittelu (Odd-Even Sorting) Kuva 2.26 esittää prosessoriverkkoa parillinen-pariton lajitteluun Jokaisen vaiheen tulot yhdistävät kaksi lajiteltua arvosekvenssiä kahdelta edellisen vaiheen modulilta Verkon kytkennät ovat sellaisia, että vastaavat parittomat ulostulot jokaisesta sekvenssistä menevät samalle komparaattorille ja vastaavat parilliset samalle Verkolla on log n vaihetta ja jokaisella vaiheella on yksi taso enemmän vartailijoita (comparators) kuin edellisellä vaiheella (1-2-3: kts. kuva 2.26) Suoritusaika vastaa vertailutasojen lukumäärää, eli 1 + 2 +... + log n = log n(log n + 1)/2, eli aritmeettisen sarja summa, kun laskettavia lukuja on log n kappaletta (=vaiheiden lkm.) laskenta-aika on O(log 2 n) Vertailijoiden lukumäärä on vertailutasojen lkm. O(log 2 n) kerrottuna vertailijoiden lkm. per taso O(n), eli O(nlog 2 n)
Ohjelmamuunnokset (program transformations) Ohjelmamuunnokset ovat keino tutkia ja hyödyntää tietokoneohjelmissa olevaa rinnakkaisuutta Ohjelmat muodostuvat operaatioista (laskennasta) ja kontrolliosista, joka määrää laskennan järjestyksen Ohjelmamuunnokset muuttavat laskennan rakennetta ja suoritusjärjestystä säilyttäen samalla ohjelman vastaavuuden alkuperäiseen (oikeelllisuuden) Muunnoksia on eri tyyppisiä riippuen kuinka vahva vastaavuusvaatimus on: yksinkertaisin (ja heikoin) kriteeri on syöte-ulostulo-vastaavuus syötemuuttujat kuvautuvat samoiksi ulostulomuuttijiksi kaikilla syöte-ulostulo-vastaavilla ohjelmilla Aikamuunnokset (time transformations) nopeuttavat laskentaa Avaruusmuunnokset (space transformations) sovittavat ohjelman rakenteellisesti erilaisille tietokoneille Monissa ohjelmissa on riippuvuuksia, jotka eivät ole todellisia vaan ohjelmoijan luomia ja ne voidaan poistaa Toisaalta monet riippuvuudet ovat ongelman ratkaisuun liityviä, eikä niitä voida poistaa
Ulostulo- ja epäriippuvuuksien poistaminen Ulostulo ja epäriippuvuudet ovat riippuvuuksia joita ohjelman tekijä voi luoda ohjelmaan: eivät ole todellisia riippuvuuksia eivät johdu todellisesta tiedonsiirrosta kahden lauseen välillä vaan siitä, että samaa muuttujaa (muistipaikkaa) on käytetty useammassa kohdassa ohjelmaa Nämä riippuvuudet voidaan poistaa ja näin tuoda esiin lisää hyödynnettävää rinnakkaisuutta ohjelmasta Tekniikoita riippuvuuksien poistamiseen: muuttujan uudelleen nimeäminen (variable renaming) skalaarilaajennus (scalar expansion) solmunjakaminen (node splitting)
Muuttujan uudelleen nimeäminen Nimetään uudelleen osa muuttujista (esittelemällä uusia muuttujia) jotta ulostulo- epäriippuvuudet poistuvat Esim: S1: A = B * C S2: D = A + 1 S3: A = A * D Jos S3 sijaan käytetään lausetta S3 : AA = A * D ohjelman toiminta ei muutu mutta ulostulo- ja epäriippuvuudet poistuvat
Skalaarinlaajennus Skalaarilaajennustekniikassa silmukassa esiintyvään skalaariarvoon assosioidaan indeksi, jolloin saadaan poistettua iteraatioiden välisiä riippuvuuksia Esim.: for i to n S1: b = B(i) 2 S2: c = C(i)*B(i) S3: A(i) = b+c end epäriippuvuus iteraatioiden välillä b:n kautta epäriippuvuus iteraatioiden välillä c:n kautta ulostuloriippuvuus iteraatioiden välillä b:n kautta ulostuloriippuvuus iteraatioiden välillä c:n kautta
Jos muuttuja b korvataan taulukolla b(i) ja muuttuja c taulukolla c(i), ulostulo- ja epäriippuvuudet poistuvat: ts. iteraatioiden välillä ei ole enää riippuvuuksia, sillä b(i) ja c(i) viittaavat eri muuttujiin eri iteraatioissa (kun muuttuja luodaan i iteraatiolla, ei myöhemmillä iteraatioilla i + k, k=1,2,... viitata enää luotuun muuttujaan) iteraatioita voidaan suorittaa rinnakkain for i to n S1: b(i) = B(i) 2 S2: c(i) = C(i)*B(i) S3: A(i) = b(i)+c(i) end
Solmunjakaminen Eräät silmukat sisältävät datariippuvuussyklejä jotka voidaan poistaa käyttämällä väliaikaisia muuttujia Esim.: do i = 1,n S1: A(i) = B(i) + C(i) S2: D(i) = A(i) + 2 S3: F(i) = D(i) + A(i + 1) end
Datariippuvuusgraafista nähdään riippuvuussykli, joka vihjaa riippuvuudesta iteraatioiden välillä: tämä on kuitenkin epäriippuvuus, joka voidaan poistaa käyttämällä väliaikaista muuttujaa: do i = 1,n S0 : AA(i) = A(i + 1) S1 : A(i) = B(i)+C(i) S2 : D(i) = A(i) + 2 S3 : F(i) = D(i) + AA(i) end Datariippuvuussykli poistui jakamalla solmu S3 solmuiksi S0 ja S3 : silmukan iteraatiot ovat nyt riippumattomia toisistaan ja voidaan suorittaa samanaikaisesti kaikille i = 1,2,..,n S0: AA(1:N) = A(2:N+1) S1: A(1:N) = B(1:N)+C(1:N) S2: D(1:N) = A(1:N) + 2 S3: F(1:N) = D(1:N) + AA(1:N) A:n kopio siirtymän kanssa käytetään kopiota; ei riippuvuutta iteraatioiden välillä