Tiedon sijainti suoritusaikana
Tiedon sijaintipaikat
Ohjelman suoritusaikana viittaama tieto voi sijaita kolmessa eri paikassa. Ensinnäkin, se voi sijaita suorittimen rekisterissä. Tuolloin se voi olla joko konekäskyssä nimetty rekisteri (esim. R3), joku oletusarvoinen sisäinen rekisteri (esim. PC tai SR) tai käskyrekisterin (IR) joku kenttä (esim. vakiokenttä tai operaatiokoodi). Käskyrekisterin kentät ovat hyvin helposti saatavilla suorittimella ja niiden lukeminen on ainakin yhtä nopeata kuin konekäskyssä viitattavien rekistereiden viittaukset. Rekistereitä on korkeintaan muutama kymmen, joten kovin moni tieto ei sinne mahdu. Lisäksi rekistereihin mahtuu vain ns. skalaarimuotoinen tieto, mikä tarkoittaa yksinkertaista korkeintaan yhden tai kahden sanan mittaista tietoa. Esimerkkejä ovat eri kokoiset kokonaisluvut ja liukuluvut. Rekistereihin ei voi tallettaa esimerkiksi 1000-alkioista taulukkoa, vaan sen alkiot pitää käsitellä rekisterissä yksi kerrallaan. On tosin erikoistapauksia, joissa esim. 64 alkioisiin vektorirekistereihin voi tallettaa usean data-alkion, joita kaikkia pystyy operoimaan yhdellä kertaa vektorikäskyillä. Emme käsittele tällaisia vektorisuorittimia tällä kurssilla.
Toiseksi, tieto voi olla välimuistissa. Välimuisteja voi olla useita tasoja, joista suoritinta lähempänä olevat ovat pienempiä ja nopeampia. Välimuisteja voi olla erikseen koodille ja datalle, koska näin niistä voi saada tehokkaampia. Tällöin konekäskyt haetaan omasta käskyvälimuistista ja normaalit dataviitteet koetetaan löytää datavälimuistista. Välimuistien toimintalogiikka on täysin automaattista, eikä siihen voi mitenkään vaikuttaa suoritusaikana.
Kolmanneksi, tieto voi olla muistissa. Kaikki tieto mahtuu tänne, mutta välttämättä kaikki ohjelman tarvitsema tieto ei silti ole aina paikalla. Pääosa ohjelman käyttämästä tiedosta on muistissa, josta se haetaan tarvittaessa rekistereihin ja välimuistiin. Usein välimuistiin haetaan tietoa myös spekulatiivisesti arvaamalla, että kohta tuotakin tietoa varmaan käytetään. Esimerkiksi, kun viittaat taulukon ensimmäiseen alkioon, niin siinä yhteydessä välimuistiin voidaan hakea saman taulukon toinen, kolmas ja neljäskin alkio.
Suoritusaikainen tieto ei voi olla massamuistissa, koska tiedon hakemiseen sieltä kuluu liikaa aikaa. Jos ohjelma haluaa viitata massamuistissa olevaan tietoon, ohjelman suoritus keskeytetään ja tarvittava tieto kopioidaan muistiin. Tämän jälkeen suoritus voi jatkua, mutta nyt viitattu tieto löytyykin muistista. Tiedon siirron aikana suoritin suorittaa muita ohjelmia.
Eri paikoissa olevaan tietoon viittaaminen
Suorittimen normaalissa rekisterissä olevaan tietoon viitataan konekäskyissä nimeämällä kyseinen rekisteri. Samoin käskyrekisterissä (IR) olevaan vakioon tai osoitteeseen viittaaminen on nopeata, koska tieto on jo valmiiksi suorittimella. Sama pätee paikanlaskuriin (PC) viittaamiseen. Molemmat rekisterit löytyvät suorittimen kontrolliyksiköstä ja IR:n eri kentät ovat siellä valmiiksi eroteltu lukemista varten. PC:n arvoa pitää joissakin konekielissä pystyä lukemaan myös konekäskyn suorituksen aikana, koska hyppy- tai haarautumiskäskyn osoite olla määriteltynä suhteellisena osoitteena PC:n suhteen. Nämä ovat nopeimmat tavat viitata tietoon.
Välimuistissa olevaan tietoon ei voi suoraan viitata, koska suoritusaikana ei voi tietää, löytyykö tieto välimuistista vai ei. Muistissa olevaan tietoon viitatessa aina tarkistetaan ensin, josko tieto löytyisikin välimuistista. Jos se löytyy, niin hyvä niin ja tieto on nopeasti saatavilla. Ohjelmakoodissa voi lisätä todennäköisyyttä tiedon löytymiseen välimuistista, jos koodin kirjoittaja ymmärtää välimuistin toimintatapaa. Välimuistit pyrkivät pitämään saatavilla viime aikoina viitattuja ja niiden lähellä olevia muistialueita. Hyvä koodaaja pystyy hyödyntämään tätä tietoa ohjelman toiminnan nopeuttamiseksi.
Muistissa olevaan tietoon viitataan käyttäen suorittimen ymmärtämiä muistinosoitusmuotoja, joista suora (indeksoitu) muistiviite on yleisimmin käytetty. Epäsuoria muistiviitteitä ei nykyään useinkaan enää käytetä, koska niiden suoritus kestää niin kauan aikaa. Sen sijaan epäsuorat muistiviitteet toteutetaan yleensä kahdella suoraa muistinosoitusta käyttävällä konekäskyllä, joista ensimmäinen hakee tiedon osoitteen muistista ja toinen sitten käyttää tätä osoitetta tiedon lukemiseen tai kirjoittamiseen. On helpompi toteuttaa nopeita suorittimia, jos kaikkien konekäskyjen (ja niiden osien) suoritus kestää yhtä kauan.
Tiedon sijainti ja siihen osoittaminen
Huom: nämä ovat irrallisia käskyjä - ne eivät muodosta ohjelmaa.
Kaikkien käskyjen tulos talletetaan rekisteriin r2.
ptrX dc 453828 -- symbolin ptrX arvo on (osoitin)muuttujan ptrX
osoite. (Osoitin)muuttujan ptrX arvo on
muistissa olevan tiedon osoite
Tbl ds 200 -- symbolin Tbl arvo on 200-alkioisen taulukon
ensimmäisen alkion osoite
load r2, =80 -- luku 80 on IR:n vakio-osassa
load r2, Tbl(r1) -- Tbl(r1) on suora muistinosoitusviite
keskusmuistiin, osoitteeseen 280. Arvo 280 on
lukujen 200 (IR:n vakio-osa) ja 80 (rek r1)
summa. Tulos talletetaan r2:een.
add r2, =1 -- ensimmäinen operandi on r2:ssä
toinen operandi (luku 1) on IR:n vakio-osassa.
load r2, Tbl(r5) -- alkio Tbl(r5) osoitteesta 281 löytyisi
luultavasti välimuistista, koska sen viereiseen
alkioon osoitteessa 280 viitattiin juuri äsken.
load r2, @ptrX -- epäsuora muistiviite, ptrX arvo löytyy IR:n
vakio-osasta, tiedon osoite 453828 löytyy
muistista (osoitteesta ptrX), tieto löytyy
muistista ptrX:n osoittamasta osoitteesta 453828
Tiedon sijainnin vaikutus suoritusnopeuteen
Yleisesti ottaen kaikki tieto sijaitsee muistissa ja juuri nyt käsiteltävänä oleva tieto sijaitsee suorittimen rekistereissä. Tästä on se seuraus, että jokin tietty tieto (esim. muuttujan X arvo) voi sijaita sekä muistissa että rekisterissä. On ohjelmoijan vastuulla, että X:n arvon muuttuessa se tarvittaessa talletetaan myös muistiin. Muistissa sijaitseva tieto voi olla myös välimuistissa, mutta laitteisto huolehtii automaattisesti sen kirjoittamisesta muistiin tarvittaessa.
Korkean tason kieliä käytettäessä kääntäjä päättää, milloin jokin tieto pidetään missäkin rekisterissä. Se on itse asiassa hyvin vaikea ns. rekistereiden allokointiongelma, koska rekistereitä on hyvin vähän ja kuitenkin kaikki laskenta tapahtuu rekistereissä olevan tiedon varassa.
Esimerkiksi, kaikkialla näkyvän laskuri Count ja sen yläraja Limit olisi hyvä pitää rekistereissä silmukan koko suoritusajan, jos niihin viitataan vähän väliä. Jos ohjelman suorituksessa on sitten pitkä tauko, jolloin Count'iin tai Limit'iin ei tule lainkaan viittauksia, niin silloin niiden arvoja ei kannata pitää rekisterissä. Ohjelmakoodissa tiedon sijainti näkyy siinä, että viitataanko suoraan rekisteriin vai haetaanko tieto ensin johonkin rekisteriin muistista.
Esimerkki: Count ja Limit rekistereissä r1 ja r2
add r1, =1 -- kasvata muuntelumuuttujaa Count
comp r1, r2 -- testaa loopin loppuminen, Count vs. Limit?
jless loop -- hyppää, jos Count < Limit
Toisaalta, ei ole itsestään selvää, että muuttujien Count ja Limit arvot kannattaisi pitää rekistereissä juuri tämän silmukan suorituksen aikana. Rekistereitä on vähän ja niille voisi olla vielä tärkeämpääkin käyttöä. Niiden arvot voisi yhtä hyvin pitää muistissa. Koodista tulee (tältä osin) hitaampaa, koska suoritettavia käskyjä on enemmän ja ne viittaavat muistiin useammin.
Esimerkki: Count ja Limit molemmat muistissa
load r4, Count -- lisää muuntelumuuttujaa
add r4, =1
store r4, Count
load r3, Count -- testaa loopin loppuminen
comp r3, Limit
jless loop
Kolmaskin vaihtoehto on olemassa. Silmukan muuntelumuuttujan arvon voi pitää rekisterissä silmukan suoritusajan ja sitten lopuksi tallettaa muistiin. Esimerkiksi C-kielessä muuntelumuuttujat ovat tavallisia muuttujia ja niiden loppuarvon täytyy olla käytettävissä myös silmukan jälkeen. Joissakin toisissa kielissä muuntelumuuttujan arvoa ei ole määritelty silmukan päättyessä tai muuntelumuuttujaa ei ole edes määritelty silmukan ulkopuolella. Korkean tason ohjelmointikieliä on hyvin erilaisia ja niillä on merkittäviä mielenkiintoisia eroavaisuuksia!
Esimerkki: muuntelumuuttuja rekisterissä ja muistissa
load r1, =0 -- alusta muuntelumuuttuja i (r1:ssä)
loop comp r1, =50 -- testaa loopin loppuminen
jnles done
... -- for-silmukan runko (itse asia) tässä
...
add r1, =1 -- i:n lisäys ja paluu silmukkaan
jump loop
done store r1, i -- talleta i:n loppuarvo (koska ohjelmointikielen
semantiikka sitä vaatii)
On siis tapauksia, joissa ohjelmassa nimetty tieto ei sijaitse missään tällä hetkellä. Edellä mainitun silmukan muuntelumuuttujan lisäksi tällaisia tietoja ovat aliohjelmien paikalliset muuttujat ja muut tietorakenteet, jotka varataan muistista vasta aliohjelmaa kutsuttaessa ja vapautetaan muistista aliohjelmasta poistuttaessa.
Käskyrekisterin (IR) kautta jotain vakioarvoa käytettäessä kääntäjällä (koodin kirjoittajalla) on kaksi mahdollisuutta. Käskyrekisteriin voidaan laittaa itse vakio (esim. arvo 1000), joka sitten replikoidaan jokaiseen tuota tietoa käyttävään konekäskyyn. Toinen vaihtoehto on tallettaa vakio muistiin ja laittaa jokaiseen siihen viittaaviin konekäskyyn vakion osoite muistissa. Molemmilla lähestymistavoilla on etunsa ja haittansa. Konekäskyssä oleva vakiolla voi olla koko- tai tyyppirajoitus, mutta sen käyttö on nopeata. Muistissa olevaan vakioon on hitaampi viitata, mutta sitä voi tarvittaessa kuitenkin muokata.
Välimuistin käyttö on tuuripeliä, mutta siihen voi vaikuttaa. On aina tehokkaampaa käydä läpi mitä tahansa suurempaa tietomassaa samassa järjestyksessä kuin se on talletettu muistiin. Ohjelmakoodin tasolla tämä tarkoittaa hyppyjen ja haarautumisten välttämistä, mikä ei käytännössä ole lainkaan helppoa. Koodissa viitatun datan osalta se tarkoittaa, että esimerkiksi 2-ulotteisia taulukoita voi olla parempi käydä läpi riveittäin kuin sarakettain. Usein ohjelmalogiikka valitettavasti vaatii tiedon läpikäyntiä välimuistin kannalta "tehottomassa" järjestyksessä. Aina ei voi voittaa!
Yhteenveto
Toinen luku käsitteli suorittimen ja muistin toimintaa. Aluksi katsoimme vähän tarkemmin suorittimen rakennetta ja erityisesti sen toimintaa käskyjen nouto- ja suoritussyklin toteuttajana. Sen jälkeen tarkastelimme konekäskyjen eri tyyppejä ja rakennetta. Lopuksi katsoimme, kuinka tiedon sijainti suoritusaikana voi vaihdella ja kuinka se vaikuttaa ohjelmien suoritusnopeuteen.
Vastaa alla olevaan kyselyyn, kun olet valmis tämän luvun tehtävien kanssa.
Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!