Hlavní navigace

Techniky zvýšení výpočetního výkonu mikroprocesorů

16. 5. 2008
Doba čtení: 12 minut

Sdílet

V dnešní části seriálu o funkci počítačů si vysvětlíme, jakými technikami je možné zvyšovat výpočetní výkon mikroprocesorů. Ukážeme si například použití zřetězeného zpracování instrukcí, explicitního i paralelního zpracování instrukcí, podporu pro běh více vláken i mikroprocesory s více jádry.

Obsah

1. Techniky zvýšení výpočetního výkonu mikroprocesorů
2. Rozšíření bitové šířky zpracovávaných dat
3. Zvýšení počtu pracovních registrů
4. Hierarchické uspořádání paměti
5. Fronta instrukcí
6. Zřetězené zpracování instrukcí (pipelining)
7. Explicitní paralelní zpracování instrukcí (VLIW)
8. Obsah další části seriálu

1. Techniky zvýšení výpočetního výkonu mikroprocesorů

Motto: number of transistors per square inch doubles about every 18 months
(Moore)

O tom, že se výpočetní výkon mikroprocesorů, tj. počet strojových instrukcí vykonaných za jednotku času, neustále zvětšuje, není pochyb. Platí to jak pro mikroprocesory určené pro běžné desktopy, tak i pro mikrořadiče, signálové procesory a programovatelné čipy typu FPGA (těmito zajímavými součástkami se také ještě budeme zabývat, protože představují jednu z možností tvorby „open hardware“). Tento prozatím stálý výkonnostní růst bez větších výkyvů je způsoben poměrně značným množstvím vlivů. Asi nejvíce patrný je vývoj samotné technologie výroby čipů s integrovanými obvody, kde se výrobcům daří zmenšovat šířky spojů mezi jednotlivými tranzistory, s čímž souvisí i zmenšování samotných tranzistorů, snižování napájecího napětí (to má velký vliv na sníženou spotřebu procesoru a tím i míry vyzařovaného tepla) a současného zvyšování taktovací frekvence. Obecně totiž u unipolárních technologií čipů platí, že vyzářený výkon=frekven­ce×napětí2, tj. zmenšení napájecího napětí vede k radikálnímu snížení vyzařovaného tepla (dnes si nikdo ani neumí představit, jak by „topil“ mikroprocesor taktovaný současnými frekvencemi a napájený pěti volty, což byl případ všech mikroprocesorů řady x86 až do příchodu prvních Pentií).

Současně se daří zvětšovat i samotnou plochu čipu na waferu (destičce, na které se většinou napařováním vytvoří větší množství jader mikroprocesorů a která se posléze rozřeže, čímž vznikne základ mikroprocesoru, který je nanesen na nosnou destičku čipu a jsou k němu většinou za studena – tlakem – přivařeny zlaté kontakty), což spolu se zmenšující se tloušťkou tranzistorů vede k tomu, že jejich počet v každé generaci mikroprocesoru roste. Růst počtu tranzistorů, taktovacích frekvencí a také několikařádové snižování šířky spojů je ostatně patrný i z následující tabulky (zaměřené především na produktovou řadu firmy Intel, ani ne tak kvůli její oblibě, jako možnosti porovnání víceméně zpětně kompatibilních mikroprocesorů – výjimku samozřejmě představují relativně neúspěšná Itania):

Mikroprocesor Bitů Datum Tranzistorů Frekvence [MHz] Šířka spojů [nm]
4004 4 1971 2 250 0.1 10 000
8008 8 1972 3 500 0.2 10 000
8080 8 1974 6 000 2.0 6 000
8088 16/8 1979 29 000 8.0 3 000
8086 16 1978 29 000 8.0 3 000
80286 16 1982 134 000 12.5 1 500
80386 32 1985 275 000 20.0 1 500
80486 32 1989 1 200 000 25.0 1 000
Pentium 32 1993 3 100 000 66.0 800
Pentium 32 1994 3 300 000 75.0 600
Pentium 32 1995 3 300 000 120.0 350
Pentium II 32 1997 7 500 000 233.0 350
Pentium II 32 1998 7 500 000 300.0 250
Pentium III 32 1999 9 500 000 450.0 250
Pentium III 32 2000 28 000 000 533.0 180
Pentium 4 32 2000 42 000 000 1500.0 180
Pentium 4 32 2002 55 000 000 2200.0 130
Pentium 4 32 2004 125 000 000 2800.0 90
Pentium 4 32 2006 188 000 000 3200.0 65
Itanium 64 2001 25 000 000 733.0 180
Itanium 2 64 2003 220 000 000 900.0 180
Itanium 2 64 2004 592 000 000 1300.0 130
Itanium 2 64 2006 1 720 000 000 1400.0 90
Core 2 64 2006 291 000 000 1860.0 65
Core 2 64 2008 800 000 000   45

Otázkou zůstává, jakým způsobem se nové tranzistory, které lze do jednoho mikroprocesoru vměstnat, dají využít a jak to ovlivní jeho výpočetní výkon.

2. Rozšíření bitové šířky zpracovávaných dat

Pravděpodobně nejsnazší metodou zvýšení výpočetního výkonu je rozšíření počtu bitů, které mikroprocesory dokáží v každé instrukci zpracovávat. I tento trend je v tabulce uvedené v předchozí kapitole jasně patrný. Čtyřbitové mikroprocesory přestaly být, spolu s krachujícím obchodem se stolními kalkulátory, zajímavé a velmi rychle se přešlo na mikroprocesory osmibitové, které byly použity mj. v prvních herních konzolích a především domácích počítačích. Dodnes jsou jejich moderní varianty používány ve formě mikrořadičů (mikroprocesorů, které na jednom čipu mají zaintegrovánu i paměť ROM a RWM), což je oblast velmi citlivá ke zvyšování cen a příkonu. Mezi často používané mikrořadiče patří řada Intel 8051 (a jejich klony), Motorola 68HC11 nebo mikroprocesory PIC. Šestnáctibitové mikroprocesory tvořily mezistupeň k mikroprocesorům třicetidvoubitovým, které jsou (po cca dvaceti letech) v oblasti desktopu v poslední době nahrazovány mikroprocesory šedesátičtyřbi­tovými.

pc1101

Z dnešního pohledu značně primitivní struktura prvního mikroprocesoru na světě – Intel 4004 (všimněte si iniciál Federica Fagina)

3. Zvýšení počtu pracovních registrů

Další možností využití „nových“ tranzistorů, které jsou návrhářům k dispozici, je zvýšení počtu pracovních registrů. I když se to z dnešního pohledu může zdát takřka neuvěřitelné, tak se ještě v mnoha osmibitových mikroprocesorech s pracovními registry šetřilo mj. i z toho důvodu, že se jejich počet projevil na výrobní ceně (i revoluční cena MOS 6502, která odstartovala boom domácích počítačů, se mj. odvíjela od minimálního počtu pouhých tří pracovních registrů, druhým faktorem byla větší výtěžnost výroby, než jaké dosahovala konkurence, tj. především firmy Intel a Motorola). Dnes bývá soubor pracovních registrů poměrně rozsáhlý (protože registry zabírají na ploše čipu minimální místo, například při porovnání s moderní ALU či FPU) a tvoří tak vlastně nejrychlejší úroveň paměti.

pc1102

Struktura osmibitového mikroprocesoru MOS 6502 je již o poznání složitější (barevně jsou zvýrazněny jednotlivé části – ALU, řadič, pracovní registry atd.)

4. Hierarchické uspořádání paměti

Další metodou, která do jisté míry zasahuje do celé architektury počítačů, je použití hierarchického uspořádání pamětí. Idea je vcelku prostá – vzhledem k tomu, že není z cenových aj. důvodů možné celou operační paměť vytvořit z pomocí té nejrychlejší dostupné technologie (například se jedná o bipolární statické paměti), je vytvořena hierarchická struktura, na jejímž vrcholku stojí kapacitně malá, zato však velmi rychlá paměť tvořená pracovními registry umístěnými přímo na mikroprocesoru, která je následovaná vyrovnávací pamětí (cache) první úrovně (Level 1 cache), dnes taktéž většinou umístěná na čipu mikroprocesoru. Dále se v hierarchii nachází vyrovnávací paměti druhé úrovně (Level 2 cache), které jsou poněkud pomalejší, ale zato mají větší kapacitu. Pod touto pamětí je již vlastní operační paměť, jež je z hlediska mikroprocesoru velmi pomalá (a k tomu ne zcela náhodně přístupná), ovšem její kapacita může u desktopů dosahovat řádu gigabytů.

Vzhledem k tomu, že mikroprocesory určené pro desktopy a servery podporují virtuální paměť, jde hierarchie ještě dále, protože se operační paměť rozšiřuje o několik řádů pomalejší a současně o několik řádů objemnější pevné disky. Z hlediska programátora se tato hierarchie většinou omezuje na pracovní registry a virtuální paměť tvořenou jak operační pamětí, tak i částí kapacity pevného disku (či Flash disku atd.), protože vyrovnávací paměti jsou z jeho pohledu transparentní – jejich absence sice povede k pomalejšímu běhu programu, ale samotný program nebude zapotřebí v naprosté většině případů měnit.

5. Fronta instrukcí

Vzhledem k tomu, že mikroprocesory jsou obecně rychlejší než operační paměti a využití paměti není zcela uniformní (doba trvání instrukcí měřená v počtu taktů se může lišit), začali výrobci do mikroprocesorů vkládat takzvanou frontu instrukcí. Ta funguje poměrně jednoduchým způsobem. Jedná se o skutečnou frontu, do které jsou postupně vkládány operační kódy instrukcí načítané z operační paměti. Z druhé strany fronty si operační kódy vybírá řadič. V případě, že se provádí některé složitější instrukce (například násobení), mohou se mezitím (paralelně) do fronty instrukcí načítat další operační kódy a ve chvíli, kdy se provádí rychlé operace (součet dvou registrů, inkrementace, porovnávání, logické operace) se operační kódy mohou velmi rychle číst z fronty a mikroprocesor nemusí čekat na relativně pomalou operační paměť. Fronta instrukcí se musí vyprázdnit v případě, že dojde ke změně běhu programu, tj. po příchodu přerušení, provedení instrukce skoku, skoku do podprogramu či návratu z podprogramu.

pc1103

Typické umístění fronty instrukcí

6. Zřetězené zpracování instrukcí (pipelining)

Velmi často používanou „urychlovací“ technologií je zřetězené provádění instrukcí neboli pipelining. Pro vysvětlení funkce pipeliningu si nejdříve připomeňme, jakým způsobem je na našem hypotetickém mikroprocesoru prováděna nějaká základní instrukce, například součet dvou pracovních registrů A a B. Nejprve mikroprocesor načte z operační paměti operační kód instrukce, tj. známým způsobem zakódovanou sekvenci ADD A,B. Operační kód je přenesen do řadiče, který ihned začne vykonávat všechny potřebné činnosti. Nejdříve se přenese obsah pracovního registru A do vyrovnávacího registru před ALU. Totéž se provede s obsahem pracovního registru B. Poté řadič přikáže ALU, že má provést součet obou vyrovnávacích registrů a následně (samozřejmě až poté, co ALU operaci vykoná) se výsledek uloží do pracovního registru A. Celá operace trvá několik taktů, protože se minimálně provede několik kroků:

  1. Načtení instrukce z operační paměti (fetch)
  2. Dekódování instrukce v řadiči (decode)
  3. Přenos obsahů pracovních registrů (memory access)
  4. Vlastní provedení instrukce (execute)
  5. Uložení výsledku zpět do pracovního registru (write back)
pc1104

Načtení a dekódování instrukce (fetch, decode)

Zajímavé přitom je, že se tyto kroky většinou provádí v jiné části mikroprocesoru: o načítání se stará paměťový subsystém, dekódování provádí řadič, přenosy jsou realizovány na interní sběrnici, provedení instrukce je řešeno v ALU a uložení výsledků je práce pro interní sběrnici a jeden z pracovních registrů. To jinými slovy vlastně znamená, že v jeden okamžik pracuje pouze velmi malá část mikroprocesoru a ostatní části na ní čekají.

pc1105

Práce s registry, provedení instrukce a uložení výsledků (memory access, execute, write back)

Myšlenka pipeliningu spočívá v tom, že se instrukce zřetězí v tom smyslu, že se současně zpracovává větší množství instrukcí, ovšem každá instrukce se nachází v jiné fázi zpracování. Například je možné provádět nějakou aritmetickou operaci na ALU (execute) a přitom ukládat výsledek předchozí operace do pracovního registru a současně již z operační paměti načítat operační kód instrukce následující. Vyžaduje to sice nějaké úpravy v interní struktuře mikroprocesoru (například rozdělení interní sběrnice), ovšem výsledkem je velké urychlení výpočtů. Zatímco při samostatném zpracování jedné instrukce celý krok trval dejme tomu 8 strojových cyklů, s plným využitím pipeliningu je to pouze jeden cyklus (celá instrukce samozřejmě pořád trvá oněch osm cyklů, ale v jejich průběhu se rozpracuje či ukončí sedm dalších instrukcí, takže v dostatečně velkém časovém horizontu opravdu se opravdu v každém taktu zpracuje jedna instrukce). To je samozřejmě ideální případ, protože při skocích, přerušení či kolizi registrů se musí už rozpracované instrukce „zahodit“. Této poměrně závažné problematice se budeme věnovat příště.

Pipelining je v různé podobě použit prakticky u všech typů mikroprocesorů. V některých případech se s ním programátor setká už při psaní programů v assembleru (například se jedná o zásobníkové mikroprocesory Chucka Moorea, které obsahují sério-paralelní sčítačky), ale většinou se jedná o poměrně dobře skrytou vlastnost, která se projeví jen ve specifických případech, například při zpracování přerušení nebo přepínání procesů. V každém případě se jedná o jednu z nejlepších metod (pokud je dobře implementována), protože urychlení vykonávání programů bývá velké a současně se příliš nezvětšují nároky na počet tranzistorů (jinými slovy – implementace pipeliningu výrobce moc nestojí). Ještě patrnější je situace u specializovaných obvodů, například grafických čipů, kde může mít pipeline až několik stovek řezů (kroků).

Většinou se popisuje klasický případ pipeliningu, kdy je zpracování instrukce rozděleno do pěti kroků (zhruba to odpovídá krokům uvedeným výše), ovšem v praxi může být počet kroků mnohem větší, což znamená, že jsou kroky jednodušší a tím pádem mohou trvat kratší dobu (zvyšuje se taktovací frekvence). Některé moderní mikroprocesory instrukce rozdělují na více než 30 kroků (implementace jednoho kroku, se nazývá řez).

7. Explicitní paralelní zpracování instrukcí (VLIW)

Další metoda, kterou někteří výrobci mikroprocesorů použili pro urychlení vykonávání instrukcí, spočívá v tom, že na mikroprocesoru jsou všechny výkonné jednotky umístěny paralelně vedle sebe, přičemž mohou vykonávat operace současně a nezávisle na sobě (za předpokladu, že nenastane kolize v použitých registrech). V praxi to může vypadat například tak, že na mikroprocesoru jsou umístěny dvě jednotky pro provádění aritmetických a logických operací (jedná se vlastně o klasické ALU), jedna násobička, jedna dělička a nakonec jednotka pro provedení skoků. Jakým způsobem se však těchto celkem pět výkonných jednotek bude ovládat? Klasický princip „přečíst instrukci-vykonat instrukci“ by k žádnému urychlení nevedl a jednotky by zůstávaly nevyužity. Proto je zapotřebí nějakým způsobem docílit toho, aby byly výkonné jednotky ovládány naráz a aby současně nedocházelo ke kolizím v použitých registrech (například ten samý registr nemůže být použit pro uložení výsledků dvou paralelně běžících operací).

Ve snaze o použití co nejjednoduššího (a tím i dostatečně rychlého) řadiče mikroprocesoru, použili výrobci speciální formát operačních kódů, který byl sestaven tak, že v jedné instrukci jsou uloženy operační kódy pro všechny výkonné jednotky. Výsledkem je architektura nazvaná VLIW, neboli Very Long Instruction Word. Pro náš hypotetický mikroprocesor s dvojicí ALU, jednou násobičkou, jednou děličkou a jednotkou pro provedení skoků by celá instrukce mohla vypadat například následovně:

operace pro ALU 1 operace pro ALU 2 specifikace násobení specifikace dělení konstanta kód podmínky adresa skoku

Způsob kódování jednotlivých instrukcí v instrukčním slovu je různý. Pro operace ALU se například může třemi bity specifikovat, která operace se má použít (ADD, SUB, AND, OR, XOR, NOT, ROR, ROL), dalšími čtyřmi bity první registr vstupující do operace a následujícími čtyřmi bity registr druhý. U operací násobení a dělení jsou také zadány registry (čtyři+čtyři bity) a jedním bitem je určeno, zda se má operace vůbec provádět (tj. jsou dvě možnosti, buď MUL/DIV či NOP). Konstanta v instrukčním slově je použita pro naplnění vybraného pracovního registru. Kód podmínky pro podmíněné skoky může být zakódován ve třech bitech (JC, JZ, JNC, JNZ, JMP – nepodmíněný skok atd.) a adresa skoku bývá relativní, uložená například na třináct bitů (rozsah 4kB bývá dostatečný). Ve výsledku tedy může instrukční slovo vypadat takto:

Bity Význam
3 ALU operace číslo 1
4 registr 1 pro ALU operaci
4 registr 2 pro ALU operaci
3 ALU operace číslo 2
4 registr 1 pro ALU operaci
4 registr 2 pro ALU operaci
1 MUL operace či NOP
4 registr 1 pro MUL operaci
4 registr 2 pro MUL operaci
1 DIV operace či NOP
4 registr 1 pro DIV operaci
4 registr 2 pro DIV operaci
32 konstanta
3 specifikace podmíněného či nepodmíněného skoku
13 adresa skoku

Vidíme že označení Very Long Instruction Word je zcela na místě, protože se jedná o slovo délky 88 bitů, tj. 11 bytů. A to jsem ještě vynechal operace FPU, které u některých mikroprocesorů také bývají do instrukčního slova zahrnuty. Díky tomuto „horizontálnímu“ formátu instrukcí může řadič zůstat velmi jednoduchý a navíc mohou všechny jednotky pracovat zcela paralelně. Co samotný mikroprocesor žádným způsobem neřeší (a většinou ani nedetekuje) jsou kolize pracovních registrů: ty je zapotřebí ošetřit už při vytváření strojového kódu, tj. jde o práci překladače, který má na podobné optimalizace (většinou) dostatek času. Překladač samozřejmě také musí přeskládat instrukce tak, aby bylo instrukční slovo co nejvíce zaplněné, protože pokud se nepodaří vedle sebe uložit všech pět na sobě nezávislých instrukcí, ztrácí se největší výhoda této architektury.

Předností VLIW je poměrně velká jednoduchost řadiče, který není o mnoho složitější, než řadič klasických RISCových mikroprocesorů. Dalšího urychlení zpracovávaných instrukcí je možné dosáhnout zvýšením počtu výkonných jednotek (například 4×ALU), ovšem zdaleka ne všechny programy jsou pro toto urychlení vhodné. Největší nevýhoda architektury VLIW spočívá v tom, že se veškeré optimalizace musí provádět v závislosti na konkrétní konfiguraci daného mikroprocesoru. Už jen malé změny v časování či přidání jedné výkonné jednotky způsobí, že se programy musí znovu přeložit, protože se kompletně změní instrukční slovo. Tato architektura také obecně není vhodná v případech, že se používá interpretovaný kód, protože interprety nemají (na rozdíl od překladačů) tolik času na provádění zde zcela nutných optimalizací. Tento problém je typický například při interpretaci a JIT kompilaci bytekódu JVM (tj. většinou Javy či programů napsaných v jiných jazycích přeložené do bytekódu Javy).

Z tohoto důvodu se procesory postavené na architektuře VLIW prosadily zejména v oblasti zpracování signálů a embedded zařízeních, které mají výrobci programů stoprocentně pod kontrolou a mohou pro ně cíleně vytvářet aplikace. Mnoho signálových procesorů je typu VLIW, přičemž navíc používají jednotky FPU či FXU (Floating Point Unit, Fixed Point Unit). Bližší informace o Fixed Point aritmetice jsem popsal v seriálu Fixed Point Aritmetika.

CS24_early

8. Obsah další části seriálu

Na relativně malém prostoru vyhrazeném pro jeden článek jsme si samozřejmě nemohli podrobně popsat všechny metody, které výrobci používají k urychlení práce mikroprocesorů. Na další technologie a metody se tedy ještě podíváme příště. Zejména se budeme zabývat vektorovým zpracováním instrukcí, mikroprocesory s podporou instrukcí typu SIMD (Single Instruction, Multiple Data) či naopak MISD Multiple Instruction, Single Data(), prediktory skoků, podporou pro běh více vláken (multithreading) i podporou pro běh více samostatných procesů (multi-core).

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.