Pentium II

Éppen aktuális témáról lesz szó ebben a kis irományban. Napjaink számítógép-piacának meghatározója az Intel Pentium II (továbbiakban P2) processzora, melyrôl sok szépet s jót lehet hallani. De vajon valóban olyan eget rengetô a teljesítménye, ahogy híresztelik? Ha így van, milyen technikai megoldásoknak köszönhetô ez? Miképpen is mûködik - leegyszerûsítve - egy P2? Ha ezekre a kérdésekre keresed a választ, jó helyen jársz!

Nos, a várakozásokkal ellentétben ez a cikk nem a P2-rol fog szólni. Sokkal inkább a P2-t megelôzô Intel processzorokról, az i486-rol, Pentiumról és a Pentium Pro-ról. Kezdjük is elôször az alapoknál, hogyan és miképpen képes a Pentium már 60 MHz-en megverni a 100 MHz-es i486DX4-et?

A 386 és 486 generációs procik a programvégrehajtást "klasszikus" módon végzik: az utasításokat szépen sorban, egymás után - egyesével - végrehajtják. Bár a 486-os már képes volt a CPU és az FPU utasításokat párhuzamosan végrehajtani, ez a lebegôpontos számolást igénylô programokra jó hatással volt, azonban az FPU-t nem igénylô alkalmazásokat nem tudta gyorsítani.

A processzoron belül az utasítás végrehajtás négy fázisra bontható, és mindegyikért egy-egy egység felelôs:

1) Fetch Unit
2) Decode Unit
3) Execute Unit (az összes végrehajtó egység közös neve)
4) Retire Unit

Titokzatos nevek, de mit is jelentenek?
A Fetch Unit feladata, hogy a memóriából a feldolgozás helyére, a prociba kerüljön a soron következô utasításokat tartalmazó memóriarészlet. A Decode Unit azért felel, hogy a mûvelet elvégzéséhez minden feltétel rendelkezésre álljon. Egyrészt a mûvelet elvégzéséhez szükséges operandusokat tölti be a processzorba, másrészt értelmezi, milyen mûvelet elvégzését írja elô az adott utasítás. A Decode Unit alakítja át az utasításokat az Execute Unit számára értelmezhetô un. micro operation-okre (továbbiakban uop). Az egyszerûbb utasításokból 1 uop lesz, a komplikáltabbakból 2, 3, 4 stb., az egészen bonyolultak akár több száz uopra is bomolhatnak. Ezek az uop-ok a Decode Unitot elhagyva átlépnek a Execute Unitba. Ez az egység megfogja az uopokat, és szépen sorban végrehajtja ôket, majd az eredmény átvándorol a Retire Unitba. Ebben az utolsó lépésben az eredmény a megfelelô helyre kerül (pl. a memóriába vagy portra íródik, regiszterben tárolódik stb), a végrehajtott uopok pedig kikerülnek a processzorból. Itt véget is ér a folyamat, és minden kezdôdik elölrôl.

Mar a 386/486 generációnál is alkalmaztak un. pipeline technikát, amivel lehetôvé vált, hogy a 4 egység párhuzamosan dolgozzon. így pl. amíg a Execute Unit egy uop végrehajtásával van elfoglalva, a Fetch és Decode Unitok már a következô gépi kódú utasítást hozzak be a memóriából, és szedik szét uopokra. Ezzel a módszerrel a 4 egység mûködése folyamatossá tehetô.

A Pentiumnál nagy elôrelépést jelentett, hogy az utolsó 3 egységet megduplázták, így lehetôvé vált két utasítás párhuzamos dekódolása és végrehajtása, majd az eredmény elôállítása. Itt azonban elôjön egy súlyos probléma, amit "adatfüggésnek" szoktak nevezni. Lássuk, mi is történik, ha a következô kódrészletre kerül a végrehajtás egy Pentium procin:

a=2
b=3

Ezt a két utasítást a Pentium párhuzamosan hajtja végre, vagyis az eredmény fele annyi idô alatt áll elô, mint a "hagyományos" úton.

a=2
a=3

Hoppá, itt mar gubanc van! Mindkét utasítás ugyanabba a változóba ír! A Pentium sajnos nem olyan ügyes, hogy rájöjjön, melyik utasítás van elôl, és melyik hátul, így az ilyen esetekben a párhuzamos végrehajtás helyett visszakapcsol a hagyományos módba. Tehát elôször végrehajtja az elsô, majd a második utasítást, szépen egymás után. Ennek a bonyodalomnak a neve: WAW (Write After Write) függés, mely szerencsére nagyon ritkán fordul elô a programokban, nem úgy, mint a WAR (Write After Read):

c=a
a=b

A második utasítás az elsô által olvasott helyre próbál írni, ezt is hagyományos módon hajtja végre a Pentium.

a=b+2
c=a*a

Ezt a kódrészletet sem tudja a Pentium párhuzamosan futtatni, hiszen a második utasítás eredménye az elôzô utasítástól függ (RAW - Read After Write - függés), így itt is csak a hagyományos úton mehet a végrehajtás. Mellesleg, ez egy olyan probléma, melyre nincs jobb megoldás, mint amit a Pentium tesz.

A Pentiumok másik nagy újítása, a dinamikus Branch Prediction a feltételes ugrások útjának megbecsülésére született. Erre azért volt szükség, mert az utasítások közé ékelôdô feltételes elágazásoknál megállt a pipeline-os végrehajtás, vagyis a Fetch és a Decode Unit nem tudott "elôre dolgozni", amíg az Execution Unit meg nem állapította a feltételes elágazás eredményét. Ezért egy feltételes elágazásokkal teletûzdelt programot (pl. sok-sok IF vagy CASE utasítás Pascalban vagy C-ben) a Pentium akár a 486-osoknál is lassabban hajtaná végre a Branch Prediction nélkül. A Pentium elôre megbecsüli az elágazás eredményét, és ennek megfelelôen fog elôre dolgozni a Fetch és a Decode Unit. Ha a becslés sikeres volt, akkor minden rendben, az elágazás valódi végrehajtása után a proci fennakadás nélkül dolgozik tovább. Ha azonban a becsült irány nem stimmel, akkor a Fetch és Decode Unit munkája hiábavaló volt, hiszen az általuk dekódolt utasításokra nincs szükseg, azokat egyszerûen eldobja a proci. Ettôl viszont kiürül a pipeline, és ebbôl fakadóan a programvégrehajtás átmenetileg szünetelni fog, mert az Execution Unitnak meg kell várnia, amíg a Fetch és a Decode Unitok átadják neki a most már helyes uopokat. S mindez több ciklusnyi veszteset okoz a végrehajtásban. Éppen ezért nagyon fontos a minél pontosabb ugrás elôrejelzés.

A Pentium Pro (továbbiakban P6) architektúrájában rengeteg újítást hordoz, a legtöbb a Pentium hibáinak kiküszöbölésére irányul. A WAW és a WAR függés elhárítására bevezettek a Register Renaming nevû trükköt, mely egyszerûen annyit csinál, hogy a processzor belsô regiszterkészletét kiegészíti egy csomó átmeneti tároló hellyel.

a=2
a=3

így ezt a kódrészletet is végre tudja hajtani párhuzamosan, hiszen a 2 és a 3 csak átmeneti tároló helyre fognak kerülni, és az újabb eredményt (3) fogja a Retire Unit a végleges helyre tenni (a-ba).

A másik varázslatot röviden OOOE-nek szoktak hívni, ez szép hosszan kiírva: Out Of Order Execution. A P6-ban gyökeresen megváltoztatták a végrehajtás menetét, és a 4 nagy egység mellé létrehoztak az Instruction Pool-t és a ReOrdering Buffer-t. így a Fetch Unit-ból az utasítások a Decode Unit-ba vándorolnak, innen pedig az uopok átkerülnek az Instruction Pool-ba. Ebben a nagy "medencében" szépen megfér egymás mellett akár 20 uop is! Az Execution Unit az Instruction Poolból veszi ki a feldolgozásra szánt uopokat, majd az eredménnyel együtt átrakja ôket a ReOrdering Bufferbe, ahonnét aztán a Retire Unit veszi ki ôket, és az eredményt a már ismert módon létrehozza. Ebbôl így önmagában nem származna különösebb elônye a P6-nak... Azonban az OOOE valódi jelentôsége az, hogy a processzor képes az utasításokat nem a fizikai sorrendjükben végrehajtani. Ennek gyakorlati megvalósítását segíti az Instruction Pool, hiszen abban nem sorrendben tárolódnak az uopok, hanem össze-vissza. így az Execution Unit szabadon válogathat az uopok között, emiatt a programvégrehajtás mindig optimális. Ez egy remek dolog, mivel így az összes függést "át tudja lépni" a proci, és folytathatja a kód végrehajtását a további, esetleg kevésbé bonyodalmas utasításokkal. Természetesen a Retire Unit a ReOrdering Buffer segítségével a fizikai sorrendnek megfelelôen állítja elô az eredményt, így az elôzô proci generációkkal megmarad a kompatibilitás.

a=2
b=a
c=3
d=d+1
e=e*5

Ebben az esetben az elsô utasítás végrehajtása után nem a másodikat, hanem a harmadikat fogja a P6 végrehajtani, majd ha a függés miatt lelassult elsô és második utasítás még mindig nem fejezôdött be, akkor megy szépen tovább a negyedik majd ötödik utasításra. A párhuzamos végrehajtást a P6-ban a Pentium két pipeline-ja mellett még három, speciális pipeline és a továbbfejlesztett Branch Prediction Unit is segíti. A becslés hatékonysága ugyan javult valamennyit a Pentiumhoz viszonyítva, azonban még mindig 90% körül mozog, ami nem megfelelô a P6 ultrahosszú pipeline-jához. Hiszen ha tévesztésre kerül a sor, akkor akar 10-20 elôre dekódolt utasítást kénytelen a processzor az Instruction Pool-ból eldobni, a feldolgozást minden egység elölrôl kénytelen kezdeni. A sok-sok utasítás végrehajtó pipeline mûködése jó idôre le fog állni emiatt.

A bevezetôben említettem, hogy ez a cikk nem a P2-rôl fog szólni. Most azonban, a végéhez közeledve lássuk, miben is különbözik a P2 a P6-tol, és mi lapul a P2 változatainak fantázianevei mögött:

Pentium Pro: az eddig tárgyalt proci (256 KB, 512 KB vagy 1 MB L2 cache)
Pentium II: Pentium Pro + MMX (512 KB L2 cache)
Klamath: a P2 233-300 MHz-es változatainak kódneve
Deschutes: a P2 333-450 MHz-es változatainak kódneve
Mobile Deschutes: a P2 notebookokba szánt változata
Celeron: költségcsökkentett P2 (0 KB L2 cache)
Mendocino/CeleronA: költségcsökkentett P2 (128 KB L2 cache)
Dixon: költségcsökkentett P2 (256 KB L2 cache)
Katmai: Pentium II + KNI (Intel 3D gyorsító utasítások)
P2 Xeon: a P2 szerverekbe szánt változata (512 KB, 1 MB vagy 2 MB L2 cache)

Jól látható, hogy a Pentium II nem más, mint egy MMX-szel és pár aprósággal kiegészített Pentium Pro. A P2 azonban azonos órajelen lassabb, mint a P6, mert a beépített L2 cache csak a proci órajelének felével ketyeg a P2-ben, míg a P6-ban a CPU és az L2 cache órajele megegyezik.

A P6 un. "full speed" L2 cache-et a Xeonban és Mendocinoban hozta újra vissza az Intel. A Dixon és a Katmai is ezt a vonalat fogja folytatni.


(c) 1998 Fiery

A Dune News köszönetet mond a cikkért Fiery-nek az ASMDEMO írójának és fejlesztôjének. Kösz!