Moderni muistinhallinta Transaktionaalinen muisti ja rinnakkainen roskienkeruu



Samankaltaiset tiedostot
Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

TIES542 kevät 2009 Yhteismuistisamanaikaisuus

Käyttöjärjestelmät: poissulkeminen ja synkronointi

Automaattinen muistinhallinta

11/20: Konepelti auki

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

5. Luento: Rinnakkaisuus ja reaaliaika. Tommi Mikkonen,

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

Rinnakkaisuuden hyväksikäyttö peleissä. Paula Kemppi

Ohjelmallinen transaktiomuisti

Intel Threading Building Blocks

Oppimistavoitteet kurssilla Rinnakkaisohjelmointi

Liite 1. Projektin tulokset (Semaforit Javassa) Jukka Hyvärinen Aleksanteri Aaltonen

Samanaikaisuuden hallinta

ELM GROUP 04. Teemu Laakso Henrik Talarmo

Transaktiot - kertausta

4. Luento: Prosessit ja säikeets. Tommi Mikkonen,

Hajautettujen sovellusten muodostamistekniikat, TKO_2014 Johdatus kurssiin

RINNAKKAINEN OHJELMOINTI A,

Käyttöjärjestelmät: prosessit

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

Yleisen PSCR-menetelmän toteutus ohjelmoitavalla näytönoh

House-käyttöjärjestelmä

Computing Curricula raportin vertailu kolmeen suomalaiseen koulutusohjelmaan

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. IX Suunnittelumallit Proxy, Factory Method, Prototype ja Singleton

HAAGA-HELIA Heti-09 1 (14) ICT05: Tiedonhallinta ja Tietokannnat O.Virkki Transaktionkäsittely

Muistinsiivous. TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 30. marraskuuta 2009 TIETOTEKNIIKAN LAITOS. Muistinsiivous.

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

12. Javan toistorakenteet 12.1

Arkkitehtuurikuvaus. Ratkaisu ohjelmistotuotelinjan monikielisyyden hallintaan Innofactor Oy. Ryhmä 14

11. Javan toistorakenteet 11.1

12. Javan toistorakenteet 12.1

CUDA. Moniydinohjelmointi Mikko Honkonen

7. Oliot ja viitteet 7.1

Rinnakkaisuus. parallel tietokoneissa rinnakkaisia laskentayksiköitä concurrent asioita tapahtuu yhtaikaa. TTY Ohjelmistotekniikka

Tyyppiluokat II konstruktoriluokat, funktionaaliset riippuvuudet. TIES341 Funktio-ohjelmointi 2 Kevät 2006

A TIETORAKENTEET JA ALGORITMIT

Tietorakenteet ja algoritmit

C++11 seminaari, kevät Johannes Koskinen

Osoitin ja viittaus C++:ssa

arvostelija OSDA ja UDDI palveluhakemistoina.

PRINCIPLES OF PROGRAMMING LANGUAGES - DEBUGGER

Palvelut. Sulautetut järjestelmät Luku 2 Sivu 1 (??) Sulautetut käyttöjärjestelmät

Kahden virtualisointiohjelmiston suorituskyvyn testaus (valmiin työn esittely)

kertaa samat järjestykseen lukkarissa.

Seminaari: Keskusmuistitietokannat. Keskusmuistitietokantojen samanaikaisuuden hallinta Ilkka Pullinen

Rinnakkaisohjelmistot. Liisa Marttinen Tietojenkäsittelytieteen laitos Helsingin yliopisto Kevät 2004

Sulautettujen järjestelmien skaala on niin laaja, että on erittäin vaikea antaa yleispätevää kuvausta siitä millainen on sulautettu järjestelmä.

Tietojenkäsittelyn perusteet 2. Lisää käyttöjärjestelmistä

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 16.3

Sisällys. 18. Abstraktit tietotyypit. Johdanto. Johdanto

TIE Tietorakenteet ja algoritmit 1. TIE Tietorakenteet ja algoritmit

2 Konekieli, aliohjelmat, keskeytykset

.NET ajoympäristö. Juha Järvensivu 2007

Algoritmit 2. Luento 2 To Timo Männikkö

Sovellusarkkitehtuurit

812315A Ohjelmiston rakentaminen. Asynkronisuus

Sisällys. 12. Javan toistorakenteet. Yleistä. Laskurimuuttujat

Dynaaminen muisti. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät 2017.

HELIA 1 (14) Outi Virkki Tiedonhallinta

Ongelma(t): Jotta tietokone olisi mahdollisimman yleiskäyttöinen ja suorituskykyinen, niin miten tietokoneen resurssit tulisi tarjota ohjelmoijalle,

SEPA REFAKTOROINTI Antti Ahvenlampi, 57408L Erik Hakala, 57509T

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 15.3

Rakenteiset tietotyypit Moniulotteiset taulukot

Sisällys. 15. Lohkot. Lohkot. Lohkot

Jaetun muistin muuntaminen viestin välitykseksi. 15. lokakuuta 2007

Tietokanta (database)

Rajapinnat ja olioiden välittäminen

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

DIPLOMITYÖ ARI KORHONEN

Johnson, A Theoretician's Guide to the Experimental Analysis of Algorithms.

18. Abstraktit tietotyypit 18.1

Sisällys. 16. Lohkot. Lohkot. Lohkot

Algoritmit 1. Luento 3 Ti Timo Männikkö

Ohjelmistojen mallintaminen. Luento 11, 7.12.

10 Lock Lock-lause

Käyttöjärjestelmän rakenne

Lyhyt kertaus osoittimista

Tietorakenteet ja algoritmit

Algoritmit 2. Luento 2 Ke Timo Männikkö

Yhteentoimivuusalusta: Miten saadaan ihmiset ja koneet ymmärtämään toisiaan paremmin?

Ohjelmointikieli TIE Principles of Programming Languages Syksy 2017 Ryhmä 19

Väylät. Prosessorin tie ulkomaailmaan Pienissä järjestelmissä vain yksi väylä. Osoite, data ja ohjaussignaalit Prosessori ainoa herra (master)

Jussi Klemola 3D- KEITTIÖSUUNNITTELUOHJELMAN KÄYTTÖÖNOTTO

Älysopimusten kehittäminen. Sopimus suuntautunut ohjelmointi

ohjelman arkkitehtuurista.

Monitorit. Monitori Synkronointimenetelmiä Esimerkkejä. Andrews , Stallings 5.5

Monitorit. Tavoite. Monitori Synkronointimenetelmiä Esimerkkejä. Andrews , Stallings 5.5. Minimoi virhemahdollisuuksia

Integrointi. Ohjelmistotekniikka kevät 2003

Ohjelmoinnin peruskurssien laaja oppimäärä

Käyttöjärjestelmät. Teemu Saarelainen Tietotekniikka

Ohjelmoinnin perusteet Y Python

Lyhyesti uusista DI-ohjelmista Isohenkilökoulutus to Opintoasianpäällikkö Mari Knuuttila

Ohjelmoinnin perusteet Y Python

Tietorakenteet ja algoritmit - syksy

IIO30220 Database Management / Tietokannan hallinta TAPAHTUMIEN HALLINTA JOUNI HUOTARI ( )

Muistinhallinta ohjelmointikielissä

Transkriptio:

TEKNILLINEN KORKEAKOULU Informaatio- ja luonnontieteiden tiedekunta Tietotekniikan tutkinto-ohjelma Moderni muistinhallinta Transaktionaalinen muisti ja rinnakkainen roskienkeruu Kandidaatintyö Tuure Laurinolli Tietotekniikan laitos Espoo 2008

TEKNILLINEN KORKEAKOULU Informaatio- ja luonnontieteiden tiedekunta Tietotekniikan tutkinto-ohjelma KANDIDAATINTYÖN TIIVISTELMÄ Tekijä: Tuure Laurinolli Työn nimi: Moderni muistinhallinta Transaktionaalinen muisti ja rinnakkainen roskienkeruu Päiväys: 2. joulukuuta 2008 Sivumäärä: 7 + 25 Pääaine: Ohjelmistotekniikka Koodi: T3001 Vastuuopettaja: prof. Lauri Savioja Työn ohjaaja: TkT Vesa Hirvisalo Tässä kandidaatintyössä tutkittiin transaktionaalista muistia rinnakkaisuudehallintamenetelmänä ja rinnakkaista roskienkeruuta. Työ perustuu tuoreisiin tutkimustuloksiin transaktionaalisen muistin toteutuksen ja teorian, sekä rinnakkaisen roskienkeruun alueella. Roskienkeruun osalta työn tavoitteena oli lähteä liikkeelle erilaisten keruumenetelmien ja niihin liittyvien ongelmien perusteista ja esittää lukijalle, miten erilaisia ongelmia on ratkaistu. Roskienkeruun osalta työssä keskityttiin rinnakkaisiin menetelmiin, jotka ovat joko tuotantokäytössä tai tulossa tuotantokäyttöön. Transaktionaalisen muistin osalta taas pyrittiin esittämään muita rinnakkaisuudenhallintakeinoja, sekä antamaan mielikuva niiden ja transaktionaalisen muistin käytöstä esimerkkien kautta. Lisäksi tarkasteltiin transaktionaalisen muistin toteutusstrategioita, ohjelmointitapoja ja yhteyksiä roskienkeruuseen. Avainsanat: rinnakkaisoghjelmointi, muistinhallinta, transaktionaalinen muisti, roskienkeruu Kieli: Suomi i

HELSINKI UNIVERSITY OF ABSTRACT OF TECHNOLOGY BACHELOR'S THESIS Faculty of Information and Natural Sciences Degree Program of Computer Science and Engineering Author: Tuure Laurinolli Title of thesis: Modern memory management Transactional memory and concurrent garbage collection Date: December 2 2008 Pages: 7 + 25 Professorship: Ohjelmistotekniikka Code: T3001 Supervisor: Professor Lauri Savioja Instructor: Dr. Vesa Hirvisalo This work gives an overview of what transactional memory and concurrent and parallel garbage collection are. The work is based on literary research on current developments in transactional memory implementation and theory, and parallel and concurrent garbage collection. On garbage collection the goal of this work is to demonstrate how garbage collection solves the problem of deallocating memory and to present how various garbage collection techniques solve these problems. The work concentrates on garbage collection techniques that are in production use or are expected to enter production use shortly. On transactional memory the goal is to contrast it with other concurrency management techniques and to give the reader an idea of how they are used through concrete examples. The work also explores various transactional memory implementation strategies and programming patterns related to transactional memory, and the connection of transactional memory to garbage collection. Keywords: Language: concurrent programming, parallel programming, memory management, transactional memory, garbage collection Finnish ii

Alkulause Kiitokset ohjaajalleni, Vesa Hirvisalolle, mielenkiintoisesta aiheesta ja ohjauksesta tutkimuksen harhailtua. Espoossa 2. joulukuuta 2008 Tuure Laurinolli iii

Käytetyt lyhenteet CAS CCR DSTM GHC GC MCAS HTM STM Compare And Swap; Atominen ehdollinen korvauskäsky Conditional Critical Section; Ehdollinen kriittinen osio Dynamic STM; STM, jossa käytettävät muistialueet eivät ole etukäteen määriteltyjä Glasgow Haskell Compiler; Haskell-kielen kääntäjä Garbage Collection; Roskienkeruu Multiword CAS; Monen sanan atominen ehdollinen korvauskäsky Hardware Transactional Memory; Rautapohjainen transaktionaalinen muisti Software Transactional Memory; Ohjelmallinen transaktionaalinen muisti TM Transactional Memory; Transaktionaalinen muisti iv

Sisältö Alkulause iii Käytetyt lyhenteet iv 1 Johdanto 1 2 Muistinhallinta 3 2.1 Yleistä................................. 3 2.2 Roskienkeruu............................. 4 2.3 Roskienkeruun toteutus........................ 5 2.4 Rinnakkaisuus ja roskienkeruu.................... 7 3 Rinnakkaisuuden hallinta 10 3.1 Kriittiset osiot............................. 10 3.2 Lukot................................. 11 3.3 Lukottomat algoritmit........................ 12 3.4 Transaktionaalinen muisti...................... 13 4 Transaktionaalinen toteutus ja käyttö 15 4.1 Transaktionaalinen koodi....................... 15 4.2 Transaktionaalisen muistin toteutus................. 16 4.3 Transaktionaalisen muistin hyödyntäminen sovelluskoodissa.... 17 4.4 Transaktionaalinen muisti ja roskienkeruu............. 18 v

5 Yhteenveto 20 Kirjallisuutta 22 vi

Luku 1 Johdanto Muistinhallinta on vaikea ja tärkeä ongelma. Tietokonejärjestelmän kannalta on tärkeää, että eri prosessit pääsevät lukemaan ja kirjoittamaan vain omaa muistiaan. Sovellusohjelmoijan kannalta taas on tärkeää, että sovellus varaa ja vapauttaa muistia oikein. Järjestelmätason prosessien erotus on nykyään ratkaistu virtuaalimuistilla, jonka käyttöjärjestelmä ja rauta yhteistyössä toteuttavat. Virtuaalimuisti tarkoittaa jokaiselle prosessille esitettävää omaa yksityistä muistiavaruutta, johon muut prosessit eivät pääse vaikuttamaan. Virtuaalimuistijärjestelmä myös erottaa prosessien virtuaaliset osoiteavaruudet fyysisestä osoiteavaruudesta. Sovellusohjelman muistinvapautusongelman taas pääosin ratkaisee siirtyminen moderniin ohelmointikieleen, jonka ominaisuuksiin kuuluu roskienkeruu. Roskienkeruujärjerjestelmä vapauttaa automaattisesti muistin, kun siihen ei enää viitata ohjelmasta. Viime aikoina roskienkeruun sisältävien korkean tason ohjelmointikielten käyttö on yleistynyt huomattavasti, mutta roskienkeruu on silti myös aktiivinen tutkimusalue. Viime aikoina myös prosessorit ovat kehittyneet pikemminkin suoritusydinten määrässä kuin yksittäisten ytimien suoritusnopeudessa. Erillisten suoritusytimien koko laskentatehon hyödyntämiseen tarvitaan joko erillisiä prosesseja tai rinnakkaisohjelmointia. Erillisten yksisäikeisten prosessien tapauksessa olemassaolevat muistinhallintatekniikat riittävät, eivätkä keskenään kommunikoimattomat prosessit myöskään tarvitse synkronointia kommunikaation vuoksi. Tutkimuksen kannalta erillisiä prosesseja kiinnostavampaa onkin rinnakkaisohjelmointi. Tässä työssä rajoitun tarkastelemaan jaetun muistin rinnakkaisohjelmointia (shared memory multiprocessing). Luvussa 2 tarkastelen muistinhallintaa käyttöjärjestelmän ja ohjelmointikielen ajonaikaisen ympäristön näkökulmasta. Esittelen perinteisen virtuaalimuistin ja roskienkeräyksen, sekä roskienkeräyksen parem- 1

LUKU 1. JOHDANTO 2 paan rinnakkaistamiseen tähtääviä algoritmeja. Luvussa 3 tarkastelen rinnakkaisuuden hallintaa ohjelmoijan näkökulmasta. Esittelen rinnakkaisuudenhallintamenetelmistä lyhyesti lukkopohjaiset kriittiset osiot ja lukottomat algoritmit, sekä transaktionaalisen muistin. Luvussa 4 tarkastelen transaktionaalisen muistin toteutuksia ja vaatimuksia sen käytölle. Luku 5 on yhteenveto.

Luku 2 Muistinhallinta 2.1 Yleistä Nykyään yleisimmät käyttöjärjestelmät käyttävät muistinhallintaan virtuaalimuistia (virtual memory). Virtuaalimuistin toiminnan ymmärtäminen on oleellista, koska käyttöjärjestelmän päällä pyörivien ohjelmien muistinhallinta väkisinkin rakentuu virtuaalimuistin päälle. Virtuaalimuistin perusteista on olemassa runsaasti kirjallisuutta, esimerkiksi Tanenbaumin (2001) perusteos käyttöjärjestelmistä Modern operating systems. Seuraavissa kappaleissa esittelen virtuaalimuistin perusteet. Viimeisessä kappaleessa esittelen myös viime vuosien tutkimusta. Virtuaalimuistijärjestelmässä kullakin prosessilla on oma virtuaalinen osoiteavaruus, josta ne voivat varata muistia. Prosessin virtuaaliosoiteavaruuden käytössä olevat osat kuvataan järjestelmän fyysiseen osoiteavaruuteen, käytännössä lähinnä fyysiseen muistiin. Virtuaaliosoitteiden muuntamisesta fyysisiksi osoitteiksi huolehtii prosessorin muistinhallintayksikkö yhdessä käyttöjärjestelmän kanssa. Muistinhallintayksikkö esimerkiksi kutsuu käyttöjärjestelmän selvittämään tilanteen, mikäli prosessi yrittää käyttää muistiosoitetta, jolle ei ole kuvausta (mapping) fyysiseen muistiin. Virtuaalimuisti toteutetaan yleensä muistisivujen (memory page) avulla. Sivulla tarkoitetaan tietyn kokoista osoiteavaruuden aluetta, jonka tarkkuudella virtuaalimuistin kuvaukset fyysiseen muistiin ovat määriteltävissä. Käytännössä muistisivut ovat kooltaan kahden potensseja, jolloin kokonaisen muistiosoitteen muuntaminen vastaavan sivun osoitteeksi on helppoa osoitteen alimmat bitit nollaamalla. Osoitekuvausten lisäksi muistisivuihin liittyy usein myös muita attribuutteja, kuten muistin käyttötapa (luku/kirjoitus/suoritus). Virtuaalimuistin sivuja vastaavat fyysisen muistin sivukehykset. Kun virtuaalimuistin sivu on käytös- 3

LUKU 2. MUISTINHALLINTA 4 sä, sijaitsee sen sisältö joko jossain fyysisen muistin sivukehyksessä (page frame) tai poissa fyysisestä muistista taustamuistissa (backing store), kuten esimerkiksi kiintolevyllä. Tyypillisesti virtuaalimuistiin liittyy myös mahdollisuus jakaa fyysisiä sivuja eri prosessien välillä, eli jaettu muisti (shared memory). Jaettu muisti tarkoittaa, että eri prosessien osoiteavaruudessa olevat osoitteet kuvataan samoihin fyysisiin sivuihin. Vain luettavissa olevaa jaettua muistia käytetään esimerkiksi jaettujen kirjastojen (shared library) ohjelmakoodin jakamiseen useiden prosessien välillä fyysisen muistin säästämiseksi. (Tanenbaum, 2001) Prosessit voivat yleensä myös pyytää käyttöjärjestelmältä jaetun muistin alueita, joiden kautta ne voivat kommunikoida keskenään prosessin sisäisten säikeiden tapaan (IEEE, 2004). Näin voidaan myös prosessien välillä säikeiden tapaan käyttää jaettua muistia kommunikaatiokanavana. Tutkimuskohteita virtuaalimuistissa ovat esimerkiksi sivunkorvausalgoritmit (Paajanen, 2007). Käyttöjärjestelmässä sivunkorvausalgoritmi tekee päätöksen siitä, mikä muistisivu poistetaan (is evicted) taustamuistiin, kun prosessi haluaa käyttää sellaista sivua, jonka data ei ole muistissa, ja tyhjiä sivukehyksiä ei ole. Lisäksi virtuaalimuistiin liityviä suojaustoimintoja voidaan käyttää esimerkiksi roskienkeruun tehostamiseen (Click et al., 2005). 2.2 Roskienkeruu Yleisesti roskienkeruulla tarkoitetaan ohjelman käyttämien muistiobjektien automaattista vapauttamista. Perinteisesti ohjelmointikielissä on kaksi erillistä tapaa varata muistia: kutsupino (call stack) ja keko (heap). Pinosta varattu muisti vapautuu automaattisesti proseduurikutsun palatessa, ja keosta varattu muisti on käytettävissä, kunnes ohjelman suoritus päättyy. Koska muistia on rajallisesti, voidaan keosta varattua muistia myös vapauttaa. Perinteisesti muistin varaamisen ja vapauttamisen on tehnyt ohjelmoija erityisillä kirjastofunktioilla. Muistin vapauttaminen ohjeman suorituksen aikana on tällöin kokonaan ohjelmoijan vastuulla. Muistin vapauttamiseen liittyy kahdenlaisia ohjelmointivirheitä: roikkuva osoitin (dangling pointers) ja muistivuoto. Roikkuva osoiting tarkoittaa vapautettuun objektiin osoittavaa viitettä, ja muistivuoto objektia, johon ei enää ole viitteitä ohjelmassa, ja jota ei siten voida vapauttaa ohjelman sisältä (Varga, 2006). Mikäli muistia olisi rajattomasti, ei muistia tarvitsisi vapauttaa, eikä edellämainittuja virheluokkia olisi. Roskienkeruu luo illuusion rajattomasta muistista. Illuusio on teoriassa mahdollinen, mikäli ohjelmasta ei koskaan sen suorituksen aikana ole saavutettavissa

LUKU 2. MUISTINHALLINTA 5 enempää muistia kuin on varattavissa, ja kaikki saavuttamattomissa oleva muisti pystytään vapauttamaan. Käytännössä roskienkeräin tutkii ohjelmassa olevia muistiosoituksia ja vapauttaa automaattisesti objektit, jotka eivät enää ole saavutettavissa ohjelmasta. Vapautettu muisti voidaan varata uudelleen myöhemmin suorituksen aikana, jolloin illuusio rajattomasta muistista säilyy. Roskienkeruu määritelmän mukaan eliminoi muistivuodot vapauttamalla ohjelmasta saavuttamattomissa olevan muistin. Myös dangling pointers-ongelma eliminoituu, mikäli ohjelmointikielestä samalla poistetaan mahdollisuus vapauttaa muistia väkisin, sillä roskienkeräin ei vapauta muistia, johon on viitauksia. Roskienkeruun yleiseen problematiikkaan liittyy ero muistin elossaolon (liveness) ja saavutettavuuden (reachability) välillä. Objekti, johon ei ohjelman millään mahdollisella suorituspolulla viitata, on kuollut. Kuollut objekti voi kuitenkin olla periaattessa saavutettavissa, mikäli tarkastellaan vain objektien välisiä viittauksia. Periaatteessa roskienkeräin voisi vapauttaa kaikki kuolleet objektit, mutta ohjelman kaikkien suorituspolkujen tarkastelu on käytännössä mahdotonta. Käytännössä oletetaankin kaikki saavutettavissa roskienkerääjät poistavatkin objekteja vasta, kun niitä ei enää voi saavuttaa. Hertz ja Berger (2005) ovat tutkineet elävyys- ja saavutettavuusoraakkeleihin perustuvan eksplisiittisen muistinhallinnan ja todellisten roskankerääjien suorituskykyä. He toteavat yhteenvedossaan, että roskienkeruu on suorituskyvyltään kilpailukykyinen eksplisiittisen muistinhallinnan kanssa, kunhan muistia on käytettävissä runsaasti. Muistin vapauttamisen lisäksi roskienkerääjä voi tehdä muutakin hyödyllistä. On toteutettu roskienkerääjiä, jotka roskien tuhoamisen sijaan lisäksi tiivistävät (compact) elävät objektit yhteen muistissa. Kun objektit sijaitsevat muistissa peräjälkeen ja loppu muisti on tyhjä, voidaan muistin toteuttaa tehokkaasti siirtämällä vapaan muistin alkuun osoittavaa osoitinta. Muistin tiivistäminen myös poistaa perinteistä vapaiden muistialueiden listaan perustuvaa roskienkerääjää ja muistiallokoijaa vaivaavan muistin sirpaloitumisongelman. Sirpaloitumisella vapaan muistin pilkkoutumista pieniin osiin objektien väliin. Ongelmia sirpaloituminen aiheuttaa allokoinnin yhteydessä; muistia voi olla vapaana paljonkin, mutta siitä ei ole mitään hyötyä, ellei tarpeeksi suurta jatkuvaa muistialuetta löydy. 2.3 Roskienkeruun toteutus Roskienkeruujärjestelmiä on perinteisesti toteutettu kahdella tavalla: viittauslaskennalla (reference counting) ja viittausten seurannalla (tracing). Bacon et al. (2004) esittävät näiden olevan toistensa duaaleja, ja että nykyään kielten toteutuksissa käytetyt, tehokkaat menetelmät ovat poikkeuksetta viitauslaskennan ja

LUKU 2. MUISTINHALLINTA 6 viittausten seurannan hybridejä. Esittelen seuraavaksi lyhyesti perusnemetelmät. Viittausten seuranta on menetelmistä suoraviivaisempi. Sen perustana on suoraan saavutettavuuden käsite. Perusmuodossaan viittausten laskennassa käydään läpi kaikki ohjelman pinoissa ja globaaleissa muuttujissa, eli juurijoukossa (root set) olevat muistiviittaukset transitiivisesti, ja merkataan jokainen näin läpikäyty objekti. Objektit, joita ei merkitä, ovat saavuttamattomissa ohjelmasta, ja ne voidaan vapauttaa. Juurijoukon viittausten transitiivinen sulkeuma ja viitatut objektit muodostavat viittausgraan, joka on heikosti kytketty. Viittauslaskennassa kuhunkin muistiobjektiin liitetään laskuri, jota kasvatetaan aina kun objektiin luodaan uusi viittaus, ja vähennetään aina kun viittaus objektiin häviää. Mikäli vähennyksen jälkeen viittauslaskurin arvo on 0, ei objektiin enää ole viittauksia ohjelmasta, ja se voidaan vapauttaa. Perusmuodossaan viittauslaskenta ei välttämättä löydä kaikkia roskia, mikäli viittausgraa sisältää syklejä. Kun viimeinen viittaus ohjelmasta sykliseen rakenteeseen poistuu, on kaikkien sykliin kuuluvien objektien edelleen yli nollan. Viittauslaskurit eivät myöskään enää voi päivittyä, sillä sykli ei enää ole saavutettavissa ohjelmasta, eikä ohjelma näin ollen voi poistaa syklin sisäisiä viittauksia. Viittauslaskenta tarvitsee parikseen jonkin syklit keräävän menetelmän, mikäli tavoitteena on täydellinen roskienkeruumenetelmä. Varga kertoo gradussaan muutamista olemassaolevista ratkaisuista. Perusideana näissä on etsiä potentiaalisia syklin osia, vähentää kokeellisesti niiden viittauslaskuria, ja tutkia, purkautuuko jokin sykli. Viittausten seurantaan perustuvista kerääjistä on olemassa useita variantteja. Alun perin kerääjät vain poistivat lisäsivät merkkaamattomien objektien käyttämän muistin vapaiden muistialueiden listaan (mark-and-sweep). Myöhemmin on toteutettu myös tiivistäviä ja kopioivia kerääjiä. Kopioiva kerääjä on tiivistävän kerääjän variantti, joka kopioi elävät objektit toiseen muistialueeseen saman muistialueen sijaan. Tiivistämisen suurin ongelma on osoittimien päivittäminen tiivistämisen yhteydessä, kun objekteja siirrellään ympäriinsä muistissa. Kopioiva kerääjä helpottaa tämän osoitinten päivitystä, koska alkuperäisiä objekteja ei ylikirjoiteta. (Varga, 2006) Toinen suuri kehitysaskel ennen rinnakkaisten kerääjien yleistymistä oli sukupolviperustainen keräys. Havaittiin, että suurin osa objekteista kuolee nuorena, ja vanhat objektit yleensä elävät hyvin vanhoiksi (Varga, 2006; Ungar, 1984). Havainnon pohjalta on kehitetty sukupoleviperustaisia roskienkerääjiä (generational garbage collector), joissa muisti jaetaan erillisiin alueisiin eri ikäisiä objekteja varten. Kaikki allokointi tehdään uusien objektien alueella, josta objektit siirtyvät vanhempien objektien alueille, kun selviytyvät tarpeeksi monesta roskienkeruusta (engl. termi tenuring). Tavallisesti tarvittaessa lisää muistia kerätään

LUKU 2. MUISTINHALLINTA 7 vain nuorten objektien alue, jonka sisältämistä objekteista suurin osa on kuollut. Jotta nuoren alueen kerääminen on mahdollista, täytyy ohjelmointikielen ajonaikaisne järjestelmän pitää kirjaa viittauksista vanhalta alueelta uudelle alueelle. Tätä viittausjoukkoa nimitetään englanninkielisessä kirjallisuudessa muistetuksi joukoksi (remembered set). (Varga, 2006) 2.4 Rinnakkaisuus ja roskienkeruu Rinnakkaisuus tarkoittaa roskienkeruun yhteydessä kahta asiaa: ohjelman suorituksen ja roskienkerääjän suorituksen samanaikaisuutta (concurrent garbage collection), ja useampien roskienkeruusäikeiden rinnakkaista suoritusta (parallel garbage collection). Molemmista aiheista on olemassa varsin käytännönläheistä tutkimusta, josta tarkemmin seuraavassa. Käytännön kannalta kiinnostavin tutkimus on (Detlefs et al., 2004), joka esittelee Garbage-Firstroskienkeruumenetelmän (G1). Kyseessä on rinnakkainen ja samanaikainen roskienkerääjä, jossa muisti jaetaan blokkeihin, joista voidaan tarpeen vaatiessa kerätä jokin alijoukko. G1:n nimi tulee siitä, että blokkien kuolleisuusasteesta pidetään kirjaa, ja ensisijaisesti kerätään kokonaan tai lähes kokonaan kuolleita blokkeja. Kerääjä on suurelta osin rinnakkainen, eli ohjelman säikeet (mutator threads) voivat jatkaa suoritusta myös suurimman osan keräykseen kuluvasta ajasta. G1:n päätavoitteena on lyhentää roskienkeruun aiheuttamia suorituskatkoja. Detlefs et al. toteavat kokeissaan, että verrattuna olemassaoleviin Javan roskienkerääjiin, lyhenevät pisimmät suorituskatkot merkittävästi. G1:n läpäisykyky kuitenkin (throughput) on huonompi kuin olemassaolevien kerääjien. Muita oleellisia eroja aikaisempiin kerääjiin on G1:n blokkien runsaasta määrästä johtuva blokkien välisten välisten viittausjoukkojen aiheuttama tilakustannus. Tilakustannusta pyrittiin pienentämään ikäpohjaisella optimoinnilla, jossa allokaattoreiden käytössä olevat, eli nuorimman sukupolven blokit kerätään joka seuraavassa keräyksessä, eikä niistä ulospäin osoittavista viittauksista pidetä kirjaa. Huomattavasti G1:tä muistuttaa GHC-ympäristöön (Glasgow Haskell Compiler) toteutettu rinnakkainen, ei-samanaikainen roskienkerääjä (Marlow et al., 2008). Marlow'n et al Kerääjä perustuu myös keon jakamiseen blokkeihin, mutta blokit ovat pienempiä kuin G1:ssä. Marlow'n et al kerääjässä myöskin ikäpohjaisuus on G1:tä suuremmassa roolissa. Heidän kerääjässään vain viittauksista vanhoista blokeista nuorempiin pidetään erikseen kirjaa, ja sukupolvia on enemmän kuin G1:ssä.

LUKU 2. MUISTINHALLINTA 8 Marlow'n et al rinnakkainen kerääjä skaalautuu prosessorimäärän kasvaessa vaihtelevasti kuormaste riippuen. Parhaimmillaan kahdeksan prosessorin tapauksessa saavutettiin 4.5-kertainen keräysnopeus verrattuna samaan menetelmään yhdellä prosessorilla suoritettuna. Huonoimmillaan kahdeksan prosessorin järjestelmässä kuitenkin jäätiin alle kaksinkertaiseen nopeuteen yksiprosessorijärjestelmään nähden. Tutkimuksessa ei ole kvantitatiivista vertailua muihin keräysmenetelmiin. Click et al. (2005) esittävät rinnakkaisen ja samanaikaisen roskienkeruumenetelmän, joka muistuttaa huomattavasti G1:tä. Heidän menetelmänsä (Pauseless) vaatii rauta- ja käyttöjärjestelmätukea, eikä siten ole sovellettavissa täysin yleisesti. Pauseless on suunniteltu ja toteutettu Azul Systems-yhtiön sisällä heidän moniprosessoriarkkitehtuurilleen, eikä ole suoraan yleisesti sovellettavissa. Arkkitehtuuri sinänsä on kuitenkin kiinnostava, koska se mahdollistaa n. 400 rinnakkaisen välimuistikoherentin suorittimen järjestelmien rakentamisen. Lisäksi suorittimien käskykantaan on tehty erityisesti roskienkeruuta tukevia lisäyksiä. Pauseless pyrkii samanaikaisesti lyhyisiin suorituskatkoihin, mutta mahdollistaa ilmeisesti myös korkean läpäisyn. Samoin kuin G1, Pauseless jakaa muistin lohkoihin. Lohkot vastaavat muistisivuja, mitä hyödynnetään myöhemmin päivitettäessä kopioitujen objektien osoitteita. G1:stä poiketen Pauseless ei ole ikäpohjainen, vaan koko elossa oleva muisti käydään läpi jokaisessa merkkaussyklissä. Mielenkiintoista on samanaikaisen merkkauksen toteutus siten että se ei estä ohjelmasäikeiden suoritusta kuin juurijoukon etsinnän ajaksi. Kukin ohjelmasäie suorittaa merkkauksen ollessa käynnissä jokaisen viittauksen luvun yhteydessä lukumuurin, joka merkkaa viittauksen luetuksi ja lisää sen keräääjän läpikäytävien viittausten joukkoon, mikäli sitä ei vielä oltu merkattu luetuksi. Näin vältetään ongelma silloin kun ohjelmasäie lukee merkkaamattoman viittauksen muistista ja poistaa sen muistista, mutta säilyttää viittauksen esimerkiksi pinossa. Ilman lukumuuria viitattu objekti olisi merkkausvaiheen lopussa merkkaamaton, ja siten kerättävissä, vaikka siihen olisi viittaus pinosta. Mielenkiintoista on myös laiska viittausten uudelleenohjaus kopiointivaiheen aikana. Erillistä uudelleenohjausvaihetta ei ole, vaan lopullisesti uudelleenohjaukset tehdään seuraavan keruusyklin merkkausvaiheessa. Laiska uudelleenohjaus toimii siten että kopiointivaiheen lähdesivut merkataan suojatuiksi roskienkerääjälle varatulla tasolle. Mikäli ohjelma yrittää käyttää kopioitavaa sivua kesken kopioinnin, tapahtuu suojausvirhe, ja ohjelmasäie suorittaakin kerääjän keskeytyksen. Keskeytyskoodi etsii oikean lähdesivulta oikean forwarding pointerin, ja lukee alkuperäisen sijaan sen. Mikäli kopiointia ei vielä ole suoritettu, se myöskin ensin kopioi objektin. Roskienkeruutason suojausvirheen keskeytys suoritetaan kutsuvassa säikeessä, mutta kohotetuin oikeuksin, joten kallista järjestelmäkutsua ei tarvita.

LUKU 2. MUISTINHALLINTA 9 Pauseless-algoritmia testattiin Sunin, IBM:n ja BEA:n Java-virtuaalikoneiden roskienkerääjiä vastaan. Testinä oli muokattu SpecJBB. Testeissä mitattiin transaktioiden kestoa, eli käytännössä pitkien roskienkeruutaukojen vaikutusta transaktioden kestoon, sillä transaktiot itsessään ovat lähes vakiomittaisia. Testituloksissa Pauseless on selkeästi kilpailijoitaan parempi. Yli 3 ms kestäneitä transaktioita ei Pauselessilla ollut lainkaan, kun lähimmäksi päässeellä BEA:lla niitä oli yli 20%. Lisäksi on huomattava, että BEA:n yli 3 ms kestäneet transaktiot kestivät lähes kaikki vähintään noin 100 ms. On huomioitava, että testitulokset eivät ole helposti toistettavissa, koska ainoa Pauseless-toteutus vaatii erikoista rautaa toimiakseen, ja koska tarkkoja tietoja käytetystä testistä ei ole. Silti tulokset nähdäkseni puhuvat Pauselessin ja sen pohjalla olevan arkkitehtuurin puolesta. Kaikki esitelly rinnakkaisuuteen ja samanaikaisuuteen pyrkivät roskienkeruualgoritmiet perustuvat muistin jakamiseen aiempia sukupolviperustaisia algoritmeja ueampaan osaan. Kaikki myös vaikuttavat onnistuvan tavoitteissaan nopeamman tai vähäkatkoisemman roskienkeruun suhteen. Keräimistä G1 on edelleen kehitysasteella, mutta sen pitäisi olla mukana seuraavassa Sunin Java-virtuaalikoneen versiossa - ainakin se löytyy jo OpenJDK:n versionhallinnasta. Vastaavasti Pauseless on ollut jo vuosia tuotantokäytössä, ja GHC:n kerääjä on mukana GHC:n versiossa 6.10.1.

Luku 3 Rinnakkaisuuden hallinta 3.1 Kriittiset osiot Rinnakkaisohjelmoinnin perusongelma ovat kilpatilanteet (race condition). Kilpatilanne on hieman harhaanjohtava nimitys, jolla yleensä tarkoitetaan mahdollisuutta päätyä kilpaan (race). Kilpaongelma (race hazard) on kuvaavampi, mutta vähän käytetty termi, eikä suomennos ole vakiintunut. Kirjallisuudessa termille on esitetty erilaisia tarkkoja määritelmiä, joita Netzer ja Miller (1992) selvittävät tutkimuksessaan. Tässä luvussa tarkoitan kilvalla Netzerin et al. käyttämää termiä data race. Kilpa on tilanne, jossa säikeiden suoritusjärjestys (scheduling) vaikuttaa ohjelman suorituksen oikeellisuuteen, vaikka säikeiden suorittamien toimintojen suoritusjärjestys itsessään ei ole oikeellisuuden kannalta oleellinen. Esimerkiksi lipunmyyntijärjestelmän varatessa samanaikaisesti paikkoja kahdelle eri asiakkaalle ei lopputuloksen oikeellisuuden kannalta ole merkitystä, missä järjestyksessä paikat valitaan tai mitkä paikat kukin asiakas siten saa, mutta ohjelma ei saa varata samoja paikkoja usealle asiakkaalle. Kilpaongelmien ratkaisuna jaetun muistin rinnakkaisohjelmoinnissa ovat perinteisesti kriittiset osiot (critical section). Kriittisellä osiolla tarkoitetaan sellaista ohjelman osiota, jota vain yksi säie kerrallaan voi suorittaa. Lipunvarausesimerkissä voisi paikkojen poiminta olla kriittinen osio. Mikäli kukin muuten samanaikainen paikkoja varaava säie valitsee paikat yksi kerrallaan, ei kilpaa synny, sillä myöhemmät paikkoja varaavat säikeet näkevät aikaisempien tekemät varaukset. 10

LUKU 3. RINNAKKAISUUDEN HALLINTA 11 3.2 Lukot Kriittisiin osioihin pääsyn kontrollointiin on olemassa useita menetelmiä, joista lukot (mutex, mutual exclusion lock), sekä niiden yleistykset semaforit ja monitorit ovat yleisesti käytössä. Lukkoprimitiivin periaatteena on, että yksi säie kerrallaan voi ottaa lukon (acquire), suorittaa kriittisen osionsa ja vapauttaa sitten lukon (release). Mikäli toinen säie yrittää ottaa lukon samaan aikaan kun se on ensimmäisen hallussa, estyy toisen säikeen suoritus (engl. the other thread blocks) siihen asti kunnes ensimmäinen säie vapauttaa lukon. (Tanenbaum, 2001) Lukot ratkaisevat rinnakkaisen datankäsittelyn ongelman muuntamalla sen peräkkäiseksi. Suorituskykymielessä peräkkäiseen suoritukseen siirtyminen poistaa kaiken rinnakkaisuudesta saatavan hyödyn, ja lisäksi lukkojen käsittely itsessään hidastaa ohjelman suoritusta. Käytännössä voidaan rinnakkaisuutta lisätä pienentämällä lukkojen vaikutusaluetta - puhutaan karkeasta ja hienojakoisesta lukituksesta (coarse-grained ja ne-grained locking). Lipunvarausjärjestelmässä esimerkki karkeasta lukituksesta olisi aiemmin mainittu koko paikkojen valinnan sijoittaminen yhteen kriittiseen osioon. Koska vain paikkojen valinnan ulkopuolinen osa varausprosessista voitaisiin suorittaa rinnakkain, karkea lukitus rajoittaisi saavutettavaa rinnakkaisuutta. Hienojakoinen lukitus taas voisi lipunvarausjärjestelmässä tapahtua siten että jokaiseen paikkaan liittyisi lukko. Säie ottaisi kaikkien haluamiensa paikkojen lukot haltuunsa, merkitsisi paikat varatuksi, ja vapauttaisi lopuksi lukot. Tällöin paikanvaraussäikeet voisivat parhaimmillaan toimia rinnakkain ilman kilpailua lukoista (contention), mikäli ne eivät yrittäisi varata samoja paikkoja. Hienojakoiseen lukitukseen liittyy kuitenkin ongelmia ohjelman suorituksen oikeellisuuden kanssa. Tarkastellaan lipunvarausesimerkin tilannetta, jossa asiakkaat A ja B yrittävät varata kahta paikkaa, kun täsmälleen kaksi paikkaa on vapaana. Mikäli säie A ottaa paikan 1 lukon ja säie B ottaa paikan 2 lukon, ja tämän jälkeen säie B yrittää ottaa paikan 1 lukon ja säie A paikan 2 lukon, päädytään lukkiumaan (deadlock). Lukkiumassa olevia säikeitä ei voi suorittaa, koska ne kaikki odottavat jonkin toisen lukkiumaan kuuluvan säikeen hallussa olevaa lukkoa. Lipunvarausesimerkissä ongelmalta voitaisiin välttyä esimerkiksi ottamalla paikkojen lukot aina paikkojen numerojärjestyksessä (Tanenbaum, 2001, 3.6.4). Aina lukoilla ei kuitenkaan ole luonnollista järjestystä, mikä rajoittaa kriittisten osioiden käyttökelpoisuutta rinnakkaisuudenhallinnassa toteutuksessa. Erityisesti lukkojen käyttö kirjastojen sisällä on ongelmallista, mikäli kirjastot voivat esimerkiksi kutsua toisia kirjastoja ristiin, tai aiheuttaa vastakutsuja (callback) takaisin sovelluksen koodiin. Esimerkiksi Kahden lukkoja sisäisesti käyttävän, risitiin toisiaan kutsuvan kirjastoa johtaa helposti lukkiumaan, jos kirjastot voivat joutua

LUKU 3. RINNAKKAISUUDEN HALLINTA 12 odottamaan kumpikin toistensa lukkoa pitäen samaaan aikaan omaa lukkoaan hallussaan. 3.3 Lukottomat algoritmit Lukkojen aiheuttamia rinnakaisuusrajoituksia on vältetty suunnittelemalla ohjelman käyttämät tietorakenteet siten että niitä voidaan käsitellä rinnakkaisesti ilman lukkoja. Yksinkertaisista tietorakenteista, kuten jonoista on olemassa tehokkaita lukottomia versioita, esimerkiksi Michaelin ja Scottin (1996) lukoton jono. Lukottomien algoritmien toteutuksia on myöskin jo yleisten ohjelmointikielten standardikirjastoissa. Esimerkiksi Java-kielen standardikirjaston java.util.concurrentpaketti sisältää useita kokonaan tai osittain lukottomia rinnakkaisia tietorakenteita (Inc., 2008) ja CAS-semantiikan tarjoavia tyyppejä. Intel on julkaissut C++kielelle vastaavan rinnakkaisohjelmointikirjaston (Corporation, 2008). Monimutkaisten tietorakenteiden manipulointi ilman lukkoja kuitenkin on vaikeaa. Fraser (2004) toteaa, että tällaisia algoritmeja ole juurikaan julkaistu. Monimutkaisten tietorakenteiden manipuloinnin tekee vaikeaksi prosessorien rajoittunut käskykanta. Fraser toteaa erityisesti, että monimutkaisten tietorakenteiden käsittely ja kriittisten osioiden korvaaminen pelkkiä primitiivikäskyjä käyttäen on epäkäytännöllistä (Fraser, 2004, luku 2.4). Hän ehdottaa ratkaisuksi laajempia atomisia muistioperaatioita: MCAS-primitiiviä (Multiword Compare And Swap) ja transaktionaalista muistia. Yksinkertaisten tietorakenteiden tapauksessa erityisesti kyseistä tietorakennetta varten suunnitelluilla lukottomilla algoritmeilla voidaan kuitenkin sopivissa olosuhteissa saavuttaa huomattavasti parempi suorituskyky kuin esimerkiksi transaktionaalisella muistilla (Fraser, 2004, kuva 6.1). Onneksi lipunvarausesimerkin tietorakenne on yksinkertainen. Lukottomasti sen voi toteuttaa atomisen korvauskäskyn (compare and set, CAS) avulla. CASkäskyllä voidaan vaihtaa yhden muistipaikan arvo atomisesti ja ehdollisesti toiseksi. Mikäli muistipaikan nykyinen arvo on odotettu. Mikäli muistipaikan nykyinen arvo poikkeaa odotetusta, CAS-käsky ei korvaa sitä uudella arvolla, vaan arvon poikenneen odotetusta. CAS-käskyillä lipunvarausesimerkki voidaan toteuttaa samaan tapaan kuin hienojakoisella lukituksella, mutta ilman lukkoja. Yrittäessään varata paikan säie yrittää korvata vapaa-statuksen varattu-statuksella. Jos korvaus onnistuu, säie tietää että paikka on varattu sille, ja jos se epäonnistuu, säie tietää paikan olleen jo varattu, ja voi esimerkiksi yrittää uudelleen eri paikalla.

LUKU 3. RINNAKKAISUUDEN HALLINTA 13 3.4 Transaktionaalinen muisti Transaktionaalisen muistin (transactional memory, TM) käsite on peräisin Herlihyn ja Mossin (1993) rautapohjaisesta transaktionaalisesta muistista. Transaktionaalisella muistilla (TM) tarkoitetaan yleisesti useiden muistioperaatioiden suorittamista tietokannoista tutulla tavalla atomisesti ja sarjallistuvasti (atomic, serializable). Atomisuus tarkoitetaan että joko kaikki transaktion sisällä olevat muistioperaatiot suoritetaan, tai mitään ei suoriteta. Sarjallistuvuus puolestaan että transaktiot suoritetaan siten että samanlainen suoritus (execution) saataisiin suorittamalla ne peräkkäin ilman rinnakkaisuutta (Herlihy ja Moss, 1993). Herlihyn transaktionaalisella muistilla lipunvarausesimerkin rinnakkaisuudenhallinta voitaisiin toteuttaa yksinkertaisesti suorittamalla paikkojen vapauden tarkistaminen ja varatuksi merkkaaminen transaktiossa. Joko kaikki varattavat vapaat paikat saadaan merkattua varatuksi, tai sitten transaktio epäonnistuu, ja voidaan yrittää varata uudet paikat. Herlihyn ja Mossin transaktionaalinen muisti ajateltiin toteutettavaksi prosessorin sisällä erikoiskäskyillä. Shavit ja Touitou (1995) toteuttivat vastaavan transaktiojärjestelmän ohjelmallisesti, ja nimesivät sen ohjelmalliseksi transaktionaalisen muistin (software transactional memory, STM). Shavitin ja Touitoun menetelmä rajoittuu kiinteästi määriteltyihin transaktioihin, ja he pitävät menetelmäänsä lähinnä monen sanan CAS-käskynä. Vuonna 2003 Herlihy ym. kehittivät dynaamisen STM:n (Dynamic STM, DSTM), joka toimii sanatason sijaan objektitasolla. Dynaamisen STM:n etuna aikaisempiin staattisiin STM:iin on soveltuvuus linkkipohjaisten rakenteiden, kuten puiden, käsittelyyn (Herlihy et al., 2003). Shavitin ja Touitoun STM:stä ei juurikaan olisi lipunvarauksen toteutuksessa apua, mutta DSTM:llä toteutus olisi samankaltainen kuin Herlihyn ja Mossin TM:llä. Myös erilaisia laajennuksia transaktioiden semantiikkaan on esitetty. Harris ja Fraser (2003) esittävät ehdollisten kriittisten osioiden (conditional critical section, CCR) käyttöä aikaisempien transaktioiden sijaan. Erona aikaisempiin transaktionaalisiin muisteihin on, että transaktio voidaan suorittaa ehdollisesti siten että transaktiota suorittava säie blokkaa (blocks) odottaen ehdon täyttymistä ja suoritetaan automaattisesti ehdon täyttyessä. (Martin et al., 2006) esittelevät kaksi erilaista transaktioiden atomisuuteen liittyvää semantiikkaa, sekä erilaisia ongelmia liittyen kriittisten osioiden korvaamiseen transaktioilla ja transaktioiden ja CCR:ien yhdistelemiseen (composition). Heidän tutkimuksensa jälkeen on julkaistu useita TM:n semantiikkaan pureutuvia tutkimuksia (Smaragdakis et al., 2007; Guerraoui ja Kapalka, 2008; Maessen ja Arvind, 2007). Attiyan (2008) mukaan vakaata teoreettista pohjaa ei kuitenkaan ole. Eräänä transaktionaalisen muistin etuna lukkopohjaisiin kriittisiin osioihin näh-