Superskalaarisen suorittimen suunnittelussa yhden suorittimen sisällä käytetään rinnakkaislaskennan muotoa, jota kutsutaan käskytason rinnakkaistoiminnaksi ja jonka ansiosta samalla kellotaajuudella voidaan tehdä enemmän työtä. Tämä tarkoittaa sitä, että CPU suorittaa useamman kuin yhden käskyn kellojakson aikana suorittamalla useita käskyjä samanaikaisesti (ns. käskynjako) päällekkäisissä toiminnallisissa yksiköissä. Kukin funktionaalinen yksikkö on vain suorittimen ytimen sisällä oleva suoritusresurssi, kuten aritmeettinen logiikkayksikkö (ALU), liukulukuyksikkö (FPU), bittisiirrin tai kerroin.
Useimmat superskalaariset suorittimet ovat myös putkijohdettuja, mutta on mahdollista, että on olemassa myös ei-pipeloitu superskalaarinen suoritin tai putkijohdettu ei-superskalaarinen suoritin.
Superskalaarista tekniikkaa tuetaan useilla suorittimen ytimen ominaisuuksilla:
- Ohjeet tulevat järjestetystä ohjeluettelosta.
- Suoritinlaitteisto voi selvittää, millä ohjeilla on mitäkin datariippuvuuksia.
- Voi lukea useita ohjeita kellojaksoa kohti
Jokainen skalaariprosessorin suorittama käsky muuttaa yhtä tai kahta datatietoa kerrallaan, mutta jokainen vektoriprosessorin suorittama käsky käsittelee useita datatietoja kerralla. Superskalaarinen prosessori on näiden kahden sekoitus:
- Kukin ohje käsittelee yhtä datatietoa.
- Jokaisessa suorittimen ytimessä on useita päällekkäisiä toiminnallisia yksiköitä, joten useat ohjeet käsittelevät samanaikaisesti riippumattomia tietoelementtejä.
Superskalaarisessa suorittimessa käskyjen jakelija lukee käskyt muistista ja päättää, mitkä käskyt voidaan suorittaa rinnakkain, ja jakaa ne suorittimessa käytettävissä oleviin moninkertaisiin toiminnallisiin yksiköihin.
Superskalaarisen suorittimen suunnittelussa pyritään parantamaan käskynjakajan tarkkuutta ja antamaan sille mahdollisuus pitää useat toiminnalliset yksiköt koko ajan kiireisinä. Vuodesta 2008 lähtien kaikki yleiskäyttöiset suorittimet ovat superskalaarisia, ja tyypillisessä superskalaarisessa suorittimessa voi olla jopa neljä ALU:ta, kaksi FPU:ta ja kaksi SIMD-yksikköä. Jos dispatcher ei pysty pitämään kaikkia yksiköitä kiireisinä, suorittimen suorituskyky heikkenee.
Miten superskalaarinen suoritin toimii käytännössä?
Superskalaarinen suoritin pyrkii kasvattamaan käskyjen suoritusmäärää per kellosyklinen suoritus (instructions per cycle, IPC) jakamalla suoritusputken useisiin rinnakkaisiin polkuihin. Perusvaiheet ovat:
- Haun ja dekoodauksen laajentaminen niin, että useampi käsky haetaan ja dekoodataan samanaikaisesti.
- Riippuvuusanalyysi: laitteisto tai ohjelma tarkistaa, mitkä käskyt riippuvat toisistaan.
- Jakaminen (dispatch/issue): pätevät ja riippumattomat käskyt lähetetään vapaisiin funktionaalisiin yksiköihin.
- Suoritus: käsky suoritetaan sen omassa yksikössä.
- Commit/Write-back: tulokset kirjoitetaan arkkitehtonisiin rekistereihin ohjeiden järjestyksessä (riippuen arkkitehtuurista).
Monimutkaisemmat superskalaarit tukevat myös out-of-order-suoritusta: käskyt voidaan suorittaa eri järjestyksessä kuin ohjelmassa, kunhan riippuvuudet ja näkyvyys säilyvät oikein. Tällöin tarvitaan mekanismeja, kuten rekisterien uudelleen nimeäminen (register renaming) ja reorder buffer, jotta suorituksen lopputulos vastaa ohjelman määrittelyä.
Keskeiset tekniikat ja komponentit
- Käskynhakunopeus ja dekooderi — jos haetaan ja dekoodataan vain yksi käsky kerrallaan, ei ole paljon rinnakkaisuutta hyödynnettävänä. Laajemmat hakupuskuroinnit ja leveämmät dekooderit ovat tärkeitä.
- Riippuvuuksien tunnistus — laitteisto tarkistaa data-, control- ja structural-riippuvuudet, jotta ei synny kilpailua samoista resursseista.
- Rekisterien uudelleen nimeäminen — estää WAR- ja WAW-tyyppisiä ongelmia (write-after-read / write-after-write) ja mahdollistaa tehokkaamman rinnakkaisuuden.
- Out-of-order -suoritus ja commit — suorituksen järjestyksen hallinta ja näköjään järjestyksessä tapahtuva lopullinen läpivienti (commit) säilyttävät arkkitehtonisen tilan oikeana.
- Branch prediction — haarojen ennustaminen on kriittinen, koska haarat katkaisevat käskyvirran; hyvät ennustimet pienentävät putken tyhjäkäyntejä.
- Reorder buffer, reservation stations ja Tomasulo-tyyliset mekanismit — auttavat käskyjen ajoituksessa, riippuvuuksien hallinnassa ja tulosten välittämisessä turvallisesti.
- Simultaneous multithreading (SMT) — kuten Hyper-Threading, voi täyttää toiminnallisia yksiköitä ajoittain eri säikeiden käskyillä.
Edut ja rajoitukset
Edut:
- Suurempi IPC eli parempi suorituskyky samalla kellotaajuudella.
- Hyvä hyötysuhde, kun ohjelmassa on paljon riippumattomia käskyjä.
- Mahdollistaa monipuolisemman resurssien käytön: ALU:t, FPU:t ja SIMD-yksiköt voidaan pitää aktiivisina.
Rajoitukset ja haasteet:
- Riippuvuudet — runsaat datariippuvuudet rajoittavat rinnakkaisuuden määrää.
- Ohjelman luonne — harvat rinnakkaiset käskyt (esim. paljon peräkkäisiä riippuvuuksia) estävät hyödyntämisen.
- Kustannus ja monimutkaisuus — laitteiston tarpeet (ennustus, uudelleen nimeäminen, varausasemat, reorder buffer) kasvattavat piirin kokoa ja energian kulutusta.
- Haarojen ennustamisen virheet — epäonnistuneet ennusteet tuhoavat dekoodattujen ja osittain suoritettujen käskyjen edut ja aiheuttavat viivettä.
Arkkitehtonisia huomioita ja vertailu
- Superskalaarisuus vs. VLIW — VLIW-arkkitehtuureissa (Very Long Instruction Word) rinnakkaisuus määräytyy pääosin kompilointivaiheessa, kun taas superskalaarissa laitteisto tekee päätökset dynaamisesti. Tämä tekee VLIW:stä usein yksinkertaisemman laitteistollisesti, mutta vaatii älykkäämpää käännöstekniikkaa.
- Putkijohdotus — useimmat superskalaarit ovat myös putkijohdettuja; putkijohdotuksen ja superskalaarisuuden yhdistelmä tuo suurimman hyödyn, mutta myös lisää koordinoinnin tarvetta.
- Moniydin- ja heterogeeniset rakenteet — modernit järjestelmät yhdistävät superskalaarisuutta moniytimisyyteen ja erityyppisiin laskentayksiköihin (esim. SIMD, GPU), mikä parantaa suorituskykyä eri kuormissa.
Suorituskyvyn mittarit
- IPC (instructions per cycle) — keskeinen mittari, joka kertoo kuinka monta käskyä suoritetaan keskimäärin per kellosyklissä.
- Issue width — kuinka monta käskyä laite voi jakaa (issue) yhdellä kellojaksolla (esim. 2-way, 4-way, 8-way).
- Keskimääräinen putken täyttöaste — kuinka usein funktionaaliset yksiköt ovat aktiivisina.
Historia ja esimerkit
Superskalaarisuus on ollut keskeinen suunta grafi- ja yleiskäyttöisten suorittimien kehityksessä 1980–1990-luvuilta lähtien. Monet kaupalliset mikroarkkitehtuurit yhdistävät superskalaarisia ominaisuuksia, putkijohdotusta, haaranennustusta ja out-of-order -suoritusta. Nykyään lähes kaikki yleiskäyttöiset x86- ja RISC-suunnittelut ovat superskalaarisia osana monimutkaista mikroarkkitehtuuria.
Käytännön vinkkejä ohjelmoijalle
- Kirjoita koodia, joka mahdollistaa riippumattomien käskyjen esiintymisen — esimerkiksi vältä turhia rekisteririippuvuuksia ja hajauta riippuvat laskut eri vaiheisiin.
- Hyödynnä kompilaattorin optimointeja, jotka tuottavat rinnakkaisuutta hyödyntävää koodia.
- Jos suoritat numeerista laskentaa, SIMD- ja FPU-resurssit kannattaa ottaa huomioon; tietyt algoritmit hyötyvät erityisesti näiden rinnakkaisuudesta.
Yhteenvetona superskalaarinen suoritin parantaa suorituskykyä käyttämällä rinnakkaisia funktionaalisia yksiköitä yhden ytimen sisällä. Menestyksellinen toteutus vaatii kuitenkin monia tukimekanismeja riippuvuuksien hallinnasta haaranennustukseen ja rekisterien uudelleen nimeämiseen. Vaikka tekniikka on monimutkainen, se on olennainen osa modernien suorittimien tehokkuutta ja suorituskykyä.


