5. Luento: Rinnakkaisuus ja jako prosesseihin (+ lyhyesti reaaliajasta) Arto Salminen, arto.salminen@tut.fi
Agenda Rinnakkaisuus käsitteenä Perusongelmat Jako prosesseihin Reaaliaika Yhteenveto
Rinnakkaisuus Rinnakkaisuus tarkoittaa tässä yhteydessä useamman kuin yhden tehtävän suorittamista samanaikaisesti Mahdollisia hyötyjä: yksinkertaistaa testausta, työnjakoa, ajoitusta, parantaa modulaarisuutta ja suorituksen nopeutta jne
Todellinen Rinnakkaisuus Monisuoritinjärjestelmä / hajautettu järjestelmä Oheislaitteiden itsenäinen toiminta Näennäinen Moniajo Keskeytykset
Rajat hämärtymässä Multicore-prosessorit yhä pienemmissä laitteissa Erikoistarkoitukseen soveltuvat kiihdyttimet, jotka kumminkin osaavat suorittaa muitakin ohjelmia kunhan ne kirjoitetaan sopivasti Laitteen sisäinen rinnakkaisuus yleistä, vaikka ohjelmoijalle homma näyttäytyisikin peräkkäisenä Sivuvaikutukset
Esim. SSD-levyn sisäinen rinnakkaisuus Channel-level parallelism Flash-muistipaketeille useita rinnakkaisia väyliä Package-level parallelism Itsenäisiä Flash-muistipaketteja on useita Die-level parallelism Flash-muistipaketti koostuu useasta Flash-muistipiiristä Plane-level parallelism Muistipiiri koostuu kahdesta tai useammasta tasosta, jotka voivat toimia rinnakkain Sivuvaikutuksia Luku- ja kirjoitusoperaatioiden sekoittaminen -> kirjoittaminen hidastuu 4.5x hitaammaksi Hyvin suunniteltu tietokantahaku -> 5x nopeampi
Prosessien vuorovaikutus Suora vaikutus Yhteiset resurssit Viestinvälitys Epäsuora vaikutus Kilpailu yhteisistä resursseista Johtavat joukkoon hyvin tunnettuja käytännön ongelmia mutta silti on hävyttömän helppo epäonnistua!
Perusongelmat Poissulkeminen Synkronointi Lukkiutuminen Nälkiintyminen Testaus + debuggaus (oikeastaan ajoitus?)
Termejä Kriittinen alue Atominen operaatio Kilpailutilanne (race condition) Lukkiutuminen (deadlock) Nälkiintyminen (starvation)
Poissulkeminen Vain yksi ohjelma kerrallaan voi suorittaa kriittistä aluetta Keskeytyskielto Semaforit Erilaiset muut lukitusrutiinit Poissulkemisen vähentäminen tehtävä harkiten Yleensä ajoituksiin liittyvät syyt voivat pakottaa tähän (dokumentoi!) Toisaalta ylenmääräinen poissulkeminen sarjallistaa ohjelman
Synkronointi Ohjelman osa odottaa toisen ohjelman valmistumista tai oheislaitetta Semaforit Wait, Signal, Notify Pienissä sulautetuissa ohjelmissa harvoin suoraan ohjelmien välistä mutta sitäkin useammin keskeytyksiin liittyvää Synkronoinnin vähentäminen syytä tehdä harkiten Yleensä ajoituksiin liittyvät syyt voivat pakottaa tähän (dokumentoi!) Toisaalta ylenmääräinen synkronointi sarjallistaa ohjelman
Lukkiutuminen Ohjelman suoritus pysähtyy odottamatta Lukkiutuminen estyy jos neljästä ehdosta jonkin täyttyminen estetään Poissulkemisehto prosessi varaa resurssin itselleen Varaus-odotusehto prosessi ei vapauta resursseja odotuksen ajaksi Irroittamattomuusehto varatun resurssin vapauttaa vain prosessi itse Silmukkaodotusehto prosessit odottavat toisten varaamia resursseja Käytännössä hämmästyttävän helppo saada aikaan vahingossa
Nälkiintyminen Ohjelman suoritus etenee, mutta joku osa ohjelmaa ei saa koskaan suoritusvuoroa Nälkiintyminen voi tapahtua, jos ohjelmassa on poissulkemista Syynä yleensä epäsuora vaikutus, joten löytäminen testaamalla tai muuten perinteisin keinoin voi olla vaikeaa Ei selkeää ehtolistaa kuten lukkiutumiselle
Testaaminen Koska suoritus etenee rinnakkain, voivat eri säikeet/prosessit edetä eri suorituskerroilla eri tahtiin Ongelmia voi tuoda ainoastaan 1 suoritus 1.000.000.000 Eli siis vikaa tulee aina joskus Ei voida toistaa testein Vaikka virhe voitaisiin toistaakin, ei aina olla kuivilla Ongelmat voivat poistua kun testaustuki lisätään Varsinainen ongelma voi olla aivan muualla kuin missä virheen seuraukset havaitaan
Miksi rinnakkaisuus on vaikeaa? koska ohjelmoinnin opetuksessa alusta asti aivopestään kaikki ajattelemaan peräkkäisesti? Luonnostaanhan kaikki ajattelisivat muuten rinnakkaisesti ;-) koska alun perin ohjelmien avulla piti pystyä ratkaisemaan erityisesti algoritmisia ongelmia, ja melkein kaikki yleisesti käytetyt ohjelmointikielet perustuvat tälle lähtökohdalle? Ohjelmointikielen tehtävä on rajoittaa ratkaisun avaruutta ymmärrettävälle tasolle
Ratkaisuehdotuksia Ohjelmointikieli/tyyli joka peittää joitain rinnakkaisuuteen liittyviä ongelmia Esim. monet koordinaatiokielet (jotka eivät tosin ole enää kovin muodikkaita mutta joissa oli mahdollista erikseen määritellä miten tehtävät vaikuttivat toisiinsa) Funktionaalinen ohjelmointi Arkkitehtuurityyli, joka ohjaa tietynlaiseen tehtävien allokointiin Selkeä kriteeristö mitä tehdään rinnan ja mitä ei
Esimerkki koordinaatiokielestä: Linda Kommunikointi prosessien välillä tapahtuu globaaliin tuple spaceen talletettujen tuple:jen avulla Lindassa on määritelty seuraavat primitiivit: out: out(t) adds tuple t to tuple space in: in(t) attempts to match some tuple t in tuple space to the template m and, if a match is found, removes t from tuple space rd: rd(t) Similar to in except that the matched tuple remains in tuple space. The executing process suspends if it fails to match a tuple eval(expression) Similar to out except that the tuple argument to eval is evaluated after t is added to tuple space Toteutus on saatavilla useille ohjelmointikielille: C-Linda, CppLinda, Erlinda, JavaSpaces, PyLinda
Esimerkki koordinaatiokielestä: Linda, Hello World real_main(int argc,char *argv[]) { int nworker, j, hello(); nworker = atoi(argv[1]); for (j=0; j<nworker; j++) eval("worker", hello(j)); for (j=0; j<nworker; j++) in("done"); printf("hello_world is finished\n"); return(0); } hello(int i) { printf("hello, world from number %d\n",i); out("done"); return(0); }
Jako prosesseihin Toiminnallisuus Skedulointi Rakenne Tehokkuus Monisuoritinympäristössä Näiden yhdistelmä
Toiminnallinen jako Sovelluksen loogiseen toimintaan perustuva jako Etuja: ongelman ymmärtäminen, toteuttaminen ja testaaminen yksinkertaistuvat Haittoja: poissulkeminen, raskas kommunikaatio, muistinkulutus
Skedulointiin perustuva jako Samanaikaisesti suoritettavat tehtävät samassa prosessissa Etuja: reaaliaikaominaisuuksien laskenta yksinkertaistuu Haittoja: epämodulaarista, ohjelman ymmärtäminen voi vaikeutua
Rakenteeseen perustuva jako Fyysisille toimilohkoille, esim oheislaitteille, oma prosessi Etuja: jos laitteisto muuttuu -> helppo muuttaa myös koodi Haittoja: sovelluksen hallitseminen voi vaikeutua, erilaiset ajastustarpeet törmäävät
Rinnakkaistaminen Algoritmin jakaminen monisuoritinjärjestelmässä Etuja: nopeus Haittoja: vaikea suunnitella, nopeushyöty voi olla kyseenalaista Monisuoritinjärjestelmä voi olla myös epäsymmetrinen OpenCL, jne.
Erilaisista yhdistelmistä Saattavat helposti sekoittaa ohjelman perusfilosofian Muutosten tekeminen hankaloituu, jos useita mahdollisia paikkoja lisätä uutta toiminnallisuutta Pidä tyylien määrä pienenä! Dokumentoi erityisesti Yleiset suunnitteluperiaatteet Yksittäiset poikkeukset jotka kumminkin unohtuvat
Automaattinen rinnakkaistaminen Täydellisessä maailmassa ohjelmojat voisivat kirjoittaa ohjelmia kuten aina ennenkin ja kääntäjät, virtuaalikoneet ja käyttöjärjestelmät huolehtisivat laitteiston sopivasta kuormituksesta Onnistuu nykyisin tiettyyn rajaan asti datacentereissä jne, Toimii vähän huonommin pienemmissä yksiköissä, jos coreja on oikeasti paljon 2 ok, 4 ok, 8-16???, 32nok, 64NOK,
Ongelmat Prosessit harvoin oikeasti riippumattomia Epäsuora vaikutus esim. I/O järjestelmän, muistien, jne. kautta Dynaamisesti muuttuvat tilanteet voivat olla vaikeita hallita Ohjelmistotasolla ei keinoja auttaa käyttöjärjestelmää rinnakkaisuuden hallinnassa Arkkitehtuurityyli voisi auttaa; nykyiset kielet sallivat mielivaltaiset ohjelmat Koordinaatiokielet edelleen voisivat helpottaa
Yhteenveto Rinnakkaisuuden ongelmat Jakoperusteet