Hirdetés

Keresés

Hozzászólok Aktív témák

  • P.H.

    senior tag

    válasz dezz #409 üzenetére

    Ezen esetekben ki kell törölni az adott mag L1-éből a vonalat, és a másik mag L1-ébe kell átvinni. Mivel AMD-k esetében (Athlon óta biztosan) az L1 a fő cache (az L2 és az L3 is csak write-back victim cache), egy CPU-n belül csak egy helyen lehet az adott adat. Tehát nem lehet beleírni az L3-ba, mert oda csak azok az adatok kerülnek, amik »helyhiány« miatt kerülnek ki az L1-ből.

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz VaniliásRönk #412 üzenetére

    Athlon előtt (igazad van) nem nagyon volt L2 az AMD CPU-kban, Athlonból is csak a Model 3 és 4 esetében, 1 és 2 esetében csak az L2-vezérlő volt a CPU-ban. Viszont a kérdés nem ilyen egyértelmű, mivel amíg AMD esetében az L1 valósítja meg a MEOSI-protokollt, Intel esetében pedig Pentium Pro óta az L2 a MEOSI-megvalósító, az L1 csak write-through olvasási cache (, legalábbis Netburst-ig bezárólag, pont ezért hibahatáron belül ugyanannyi náluk az L1 írási sebessége, mint az L2-é, és a non-temporal módon (közvetlenül L1-be töltött) adatok írását is megsínyli nagyon, Core-ról/Core2-ről nincs teljes infóm, de a shared L2 miatt 99%, hogy így van ott is).
    Ennek következménye, hogy integrált memory controller-rel párhuzamosan az Intel-nek ki kell fejlesztenie a saját Crossbar-ját is. Vajon az az AMD szabadalma lehet? Végülis egymagos A64 óta használják...


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    Ami viszont érdekes lehet, az a következő: ''The L1 data cache can support two 128-bit loads or two 64-bit store writes per cycle or a mix of those.''
    A K8 micro-architecture megjelenése óta van egy sejtésem (bizonyítani nem tudom), hogy az az irdatlan nagy (88 vagy 120 entries) floating-point register-file 64 (esetlegesen? 80) bites egységekből épül fel, ahol két szomszédos elemet egy egységként lehet kezelni (úgy, hogy az első páros sorszámú), így jön ki a 128 bites (vagy legrosszabb FPU esetben 80 bites) register-méret. A fentiekből az következne, hogy ez a továbbiakban is így marad, mivel sehol sem látok 128 bites store-t, csak 128 bites load-ot és feldolgozást (és az x87-műveletek K8-hoz képest nem változó latency-értékeit nézve igencsak 64 bites lehet ez a register-file).

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #419 üzenetére

    Az idézeted (a .PDF-ben nem találtam ilyet) és az Optimization Guide között ott van az ellentmondás, hogy az idézet szerint ''The L3 Cache, however, is not exclusive,...'' a Guide szerint viszont ''The L3 cache is considered a non-inclusive victim cache architecture,...''. Szerintem egyszerű a dolog, hogy a jelenlegi K8 X2-k/Opteron-ok esetében is két mag közötti átvitelt a crossbar valósítja meg, ezen nem változtattak semmit.

    De tegyük fel, hogy az L3-on keresztül menne: akkor a CPU-n belüli teljesen exlusive hierarchia miatt az egyik L1-ből/L2-ből kiíródna az adat az L3-ba (, amiatt onnan egy értékes vonal esetleg kikerülne a memóriába), majd onnan a másik mag L1-ébe, viszont az L3-ban nem maradhatna, ezért az L3 vonalát azonnal érvényteleníteni kell. Tehát az L3-ban levő értékes adatok mennyisége csökkenne feleslegesen. És gyanúm szerint ez lassabb is lenne, mint az eddig bejáratott crossbar-megoldás. NUMA architecture mellett sem ritka az ilyen adatáramlás, tehát ez jelentős L3-teljesítményvesztést jelentene, mert az L3 egy része folyamatosan üres/invalid lenne.

    [mod]: ''Egyébként szerintem x86-on nincs extended prec. FP.'' :F

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Raymond #428 üzenetére

    Thx, ezzel még nem találkoztam.
    A sejtésem onnan származott, hogy K7 óta azoknak az utasításoknak, amik az XMM-register-eknek csak egyik 64 bites (vagy alsó vagy felső) felén dolgoznak, az Intel CPU-khoz képest igen kis késleltetésük van, és DirectPath-on (nem is Double-on) keresztül dekódolódnak (viszont a 80 bit-es x87-adatoknak jó lenne egy egységnek lennie). Intel-eknek ilyen esetekben mindig kell egy plusz MMX_SHIFT micro-op is.

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Raymond #433 üzenetére

    Ha a 64bit SSE utasítások alatt a skalár SSEx FP-t érted, akkor azon a téren nem változott semmi. Sőt, a vektortéren kimondott kétszeres gyorsulás is idézőjeles egy picit. Összevetettem a K8-at és a K10-et (ezeket az értékeket használom port-optimalizációnál is programozáskor):
    K8 esetében:
    - ADDPD és ADDPS (2x64 vagy 4x32 bit): 5 órajel alatt, 2 órajelenként 1-et indítva
    - ADDSD és ADDSS (1x64 vagy 1x32 bit): 4 órajel alatt, 1 órajelenként 1-et indítva
    - FADD (32, 64 vagy 80 bit x87): 4 órajel alatt, 1 órajelenként 1-et indítva
    - MULPD és MULPS (2x64 vagy 4x32 bit): 5 órajel alatt, 2 órajelenként 1-et indítva
    - MULSD és MULSS (1x64 vagy 1x32 bit): 4 órajel alatt, 1 órajelenként 1-et indítva
    - FMUL (32, 64 vagy 80 bit x87): 4 órajel alatt, órajelenként 1-et indítva

    K10 esetében:
    - ADDPD és ADDPS (2x64 vagy 4x32 bit): 4 órajel alatt, 1 órajelenként 1-et indítva
    - ADDSD és ADDSS (1x64 vagy 1x32 bit): 4 órajel alatt, 1 órajelenként 1-et indítva
    - FADD (32, 64 vagy 80 bit x87): 4 órajel alatt, 1 órajelenként 1-et indítva
    - MULPD és MULPS (2x64 vagy 4x32 bit): 4 órajel alatt, 1 órajelenként 1-et indítva
    - MULSD és MULSS (1x64 vagy 1x32 bit): 4 órajel alatt, 1 órajelenként 1-et indítva
    - FMUL (32, 64 vagy 80 bit x87): 4 órajel alatt, órajelenként 1-et indítva

    Eddig vektoros SSE-nél 2 micro-op-ra fordult az utasítás, amik egymási órajelekben léptek be a pipe-ba (DirectPath Double és 2 órajelenkénti programutasítás-szintű indítás) és az alsó 64 bites fél már a 4. órajel után elérhető volt. (Note: The low half of the result is available one cycle earlier than listed.). Most a decode és a port-issue sávszélesség lett kétszeres.
    Ami jelentősebb változás, hogy egyidőben 2 helyett 3 MOVAPS/MOVAPD (128 bites adatmozgatás) indulhat el, a 128 bites logikai műveleteket az FADD is végezheti már az FMUL mellett, és csökkent a latency-jük is, valamint a shuffle utasítások végre nem VectorPath-on fordulnak, és az FADD is végezheti őket. Sőt, úgy néz ki, hogy minden, eddig FMUL általá végzett nem szorzásjellegű műveletet megkaphat az FADD pipe is.

    dezz:
    Az lebegőpontos algoritmusok ugyanazok maradtak, csak megduplázták a számoló egységek számát. Az FADD és az FMUL pipe így változott szélességében (az MMX-hez szükséges 8 és 16 bites egységeket nem sorolom):
    - 2 db 32 bites adder/multiplier -> 4 db 32 bites adder/multiplier
    - 1db 64 bites adder/multiplier -> 2 db 64 bites adder/multiplier
    - 1 db 80 bites adder/multiplier -> 1 db 80 bites adder/multiplier
    Ha skalár SSEx vagy x87 utasítást használsz, akkor ugyanaz a megfelelő méretű 0. sorszámú egység fog számolni, a feldolgozás sebessége azonos. Sőt, ugyanarra a micro-op-ra vetíti le őket a DirectPath decoder, csak a méret SSE esetén az utasításba van kódolva, x87 esetén pedig a FPU Control Word-ből veszi a méretet (az FLDCW tudomásom szerint sorbarendező utasítás még mindig). Csak ami skalár SSEx-ben 4/5 byte-os utasítás, az x87-ben 2 byte-os.

    A párhuzamosság miatt jogosak a 2x és 4x értékek x87-tel szemben, de ennyi van benne, pont olyan marketingszöveg, mint az Advanced Media Boost, ami gyakorlatilag pont ugyanezt a szélesítést jelenti a másik oldalon.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    dezz #439: ''Miért nem jön ki a 2x-es sebesség,...?''
    Számoljunk utána: K8-on mondjuk 5 egymást követő, független ADDPS/ADDPD utasítás így fut le: 0->4 (0. órajelben indul, negyedikben fejeződik be), 2->6, 4->8, 6->10, 8->12. K10-en: 0->3, 1->4, 2->5, 3->6, 4->7. Tehát K8-ön 12 órajel alatt van kész, K10-en 7 órajel alatt. Azonos órajelen 7. utasításnál lépi át a K10 a 2x-es értéket, de ilyen hosszú, egymástól nem függő utasításokból álló kódot nehéz írni.

    ''Hmm, külön vannak 32 és 64 bites egységek? Azt gondoltam volna, 64 bites operand esetén összekapcsolva működik 2 32 bites egység.''
    Lebegőpontos egységeket nem lehet csak úgy összekapcsolni, 32-64-80 biten más-más a karakterisztika és a mantissza mérete. Ezek a lehető legnagyobb sebességre kialakított számolóegységek, a lehető legegyszerűbbek. Inkább több, speciális van egymás mellett, mint kevesebb komplex, nem pár ezer tranzisztoron múlik. Van ennél hajmeresztőbb is: ''An (on-chip) ROM-based table lookup can be used to quickly produce a 14-to-15-bit precision approximation of 1/b using just one 3-cycle latency PFRCP instruction.'' Ehhez számolóegység sincs, csak egy ROM-táblázat.

    dezz #442:
    Az FPU-egység az MMX, SSEx és x87 utasításokat csinálja, tetszőlegesen keverve, órajelenként 0, 1, 2 vagy 3 micro-op-ot indíthat, egyet minden pipe-ba, de adott micro-op csak bizonyos pipe(-ok)ban futhat. Lebegőpontos műveletek a következők lehetnek: 1x32 bit (x87, scalar SSE), 2x32 bit (3DNow!), 4x32 bit (SSE), 1x64 bit (x87, scalar SSE2), 2x64 (SSE2), 1x80 bit (x87). Ezeket legegyszerűbb az említett háromféle számolóegységgel megvalósítani. A kép valóban nagyon sematikus.

    Aztán itt megértettem, hogy mit értesz superscalar x87 alatt. Nem rossz elképzelés, de az elvisz a MISD/MIMD irányába (Multiple Instruction, Single/Multiple Data), ez nem az asztali gépek világa. SISD/SIMD alatt órajelenként egy végrehajtó egységbe egy művelet léphet csak be. (Raymond #450)

    dezz #468:
    Úgy néz ki, egy kis félreértés adódott. No, akkor gyorsan szaladjuk át a FPU egységen:
    A DirectPath és VectorPath decoder-ek órajelenként max. 3 macro-op-ra fordítják le az utasításokat, ez a CPU műveletvégző egysége. Ez a macro-op egy vagy több micro-op-ot (pl. valamilyen elemi művelet, load, store, ...) tartalmaz, valamint egyéb információkat (ilyen egyéb információ pl. a forrás- és célregister-ek nevei, eax, ST(1) vagy XMM3 szinten).
    Ezek követlenül az Instruction Control Unit-ba kerülnek, innen mennek az FPU-egységbe, majd lefutás végén ide kerülnek vissza. Az FPU egységben a Stack Map és a Register Rename lépcsőkben az ST(x) vagy XMMx formájú argumentumok leképeződnek a megfelelő registerfile-beli elemekre, tehát a micro-op-ok megkapják bemeneti és kimeneti paraméterüket (mely fizikai registereket kell olvasni és írni a 120-elemű registerfile-ban). Majd a scheduler-be kerülnek, és itt a bennük lévő micro-op-ok elindulnak a megfelelő időben a pipe-okban (FADD, FMUL vagy FSTORE).
    Tehát a CPU elemi műveleti egysége a macro-op, az execution unit-oké (ALU, AGU, FADD, FMUL, FSTORE) a micro-op. Így már remélet érthető, hogy ha egy 32 bites FADD ST(2) és egy ADDSS XMM1,XMM2 ugyanazt a micro-op-ra fordul le (az FADD ugyanannak látja), de egyáltalán nem ugyanarra a macro-op-ra. És gyakorlatilag a macro-op-ok a register-átnevezési táblázok alapján fenntartják a klasszikus register-elrendezéseket, tehát továbbra is 8 MMX/x87-veremregister van, és 8 (vagy 16) XMM-register látható programozói szinten.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #481 üzenetére

    Igazából most igazán nem értem, mit értesz a így superscalar végrehajtás. Fejtsd ki még egyszer pls.

    ''Mint ahogy az integer ALU-knál teszik is már jó ideje!'' Tudomásom szerint a Netburst volt eddig az egyetlen, amiben az ALU-k 1 órajel alatt 2 utasítást tudtak fogadni. Ezt emlékeim szerint úgy oldották meg, hogy a két egyciklusú ALU - és az ütemező (egy része) - a hivatalos magórajel kétszeresén működött.

    [offoff]Karakterisztika és mantissza kifejezéseket használja az 1988-as kiadású Pethő Ádám: A ROM-BIOS és ami mögötte van (IBM PC/XT felhasználóknak és programozóknak) Az Intel 8087 koprocesszor című fejezete. Megfelelően régi? :) [/offoff]

    akosf #476:
    A K8 és a K10 elméleti végrehajtási sebességét és gyorsulását hasonlítottam össze, ahol a decode, dispatch, és a pipe-ok működése is lényegében azonos mechanizmuson alapul. Végülis a K10 a K8-hoz képest lesz kétszeres sebességű, AMD szerint.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Raymond #472 üzenetére

    A 64 bit vs x87/MMX/3DNow!-hoz annyit, hogy az ominózus kijelentés a linkelt AMD-oldalon szerepel mindkét K8 Software Optimization Guide-ban, és ebben a K10 Guide-ban is az x87 Floating-Point Optimizations fejezet elején, mégis a fejezet további részeiben az egyes tanácsoknál szerepel, hogy:
    This optimization applies to:
    •32-bit software
    •64-bit software


    Azzal meg teljesen egyetértek, hogy x87/MMX/3DNow!-nak nincs helye kernel mode kódban. Nem volt egyszerű rájönni, hogy hogyan lehet elkerülni a Windows API egyes korábbi verzióinak (9x és NT egyaránt) ilyen szintű hülyeségeit, hogy pl. egy GlobalAlloc meghívása után nem azt találom az FPU-register-ekben, mint hívás előtt, vagy hogy a window procedure nem üres FPU-stack-et kap (fsave és fninit a kötelező kezdés, frstor a kötelező befejezés).
    Nem idevágó, de az meg egyenesen kiakasztó, hogy beállított DF-fel (std után) meghívott GMEM_ZEROINIT GlobalAlloc valamelyik 9x - talán W95 alatt - egyszerűen leállt védelmi hibával... :)

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz slett27 #486 üzenetére

    Lesz. Igaz, hogy a Software Optimization Guide for AMD Family 10h Processors című 280 oldalas doksiban összesen 7x szerepel a 3DNow! szó (ebből egy az ...are trademarks of... kifejezésben), de lesz benne. :)

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #497 üzenetére

    Azt hiszem, így már értem, hogy képzelted. Azt hittem, hogy mondjuk az egyszem FADD 4 számolóegységébe akarsz 4 scalarSSE/FPU utasítást bepasszírozni egy órajel alatt alkalomadtán.

    Az AMD ütemezői K7 óta (K7 óta biztosan) egyidejűleg összesen 9 micro-op indíthatnak egy órajel alatt párhuzamosan, 3-3 a három darab ALU-ban és a velük ábrázolásszinten valamiért szoros kapcsolatban lévő AGU-ban, 1-1-1 az FADD, az FMUL és az az FSTORE (azaz FMISC) egységekben.

    Az FPU-egységei is komplexek, talán a K7 dokumentációjában van kifejtve legrészletesebben:

    - FADD: (FP add/MMX ALU/3DNow!): The first of the three pipes is generally known as the adder pipe (FADD), and it contains 3DNow! add, MMX ALU/shifter, and floating-point add execution units.
    - FMUL: (FP mul/MMX ALU/MMX Mul/3DNow!): The second pipe is known as the multiplier (FMUL). It contains a 3DNow!/MMX multiplier/reciprocal unit, an MMX ALU and a floating-point multiplier/divider/square root unit.
    - FSTORE: The third pipe is known as the floating-point load/store (FSTORE), which handles floating-point constant
    loads (FLDZ, FLDPI, etc.), stores, FILDs, as well as many OP primitives used in VectorPath sequences. (Itt még az OP elnevezéssel illeték a micro-op-okat, csak hogy az Intel nomenklatúrájától különbözzenek, és így ebben az időben az SSE1 még 3DNow! Professional néven futott.)

    Egyébként amikor megérkeztek az első hírek a K8L megduplázott FPU-járól, akkor én is azt hittem, hogy több exetucion unit-ot megdupláznak. :)

    Ha rajtam múlna, hogy merre fejlődjön tovább az x86, akkor a következők felé vinném el:
    - a kiindulási alap egy K-family alapú micro-architecture (persze lehetőleg K10 a 128 bites FPU miatt, csak az még nem kézzelfogható most). A macro-op megközelítés maradna, sokkal hatékonyabb, mint az Intel-féle micro-op szemlélet (fusion-nal együtt), és a közös INT/FP pipe-ok se szimpatikusak.
    - a decoder-ek »mögé« tennél egy Intel-féle nagyméretű trace cache-hez hasonló macro-op cache-t tennék, levenné a L1-ről és a decoder-ekről a sokszori felesleges munkát (pl. minden ciklus-lefutás újrafordítódik). Azt ne feledjük, hogy AMD-nél a 1x-stage pipe hosszának kb. felét teszi ki a fordítás macro-op szintre.
    - behoznék az x86 utasításszintre egy speciális, 8 byte-os paraméterű feltételes ugrásokhoz kapcsolható prefix-et, így meg lehetne különböztetni már programszinten az IF-ELSE ugrásokat a ciklusszervező ugrásoktól.
    - az elágazásbecslés-szempontú ugrásvégrehajtás mellett az előbbi prefix-szel együttműködve alkalmaznám azt a (például IA-64-ben implementált) elágazáskezelési technikát, amelyben spekulatívan mindkét ágat elkezdi végrehajtani, majd a kiértékeléskor az hamis ág utasításait eldobja. Igencsak megnőne az eldobott utasítok száma, viszont ugyanennyivel (összességében nagyban) javulna a megtartott utasításokok mennyisége a mostanihoz képest. Az micro-architecture elbírná szerintem ezt a többletnyomást.
    - az egészhez átvenném az Intel Core2-es load/store szemantikáját, igencsak megnövelt, (visszavonható) store-bufferrel
    - az egészet megfejelném a Hyper-Threading egy olyan megvalósításával, ahogy nem teljesen szimmetrikus a szálvégrehajtás, hanem az OS-től kapott prioritás mentén lenne egy főszál és egy mellékszál, ami a főszál által nem használt execution- és retirement sávszélességet használhatná. Azonos prioritási szinten természetesen szimmetrikus lenne.

    Az AMD CPU-k felépítéséből ebbe az egészbe leginkább a VectorPath decoder nem illik bele (Decoding a VectorPath instruction may prevent the simultaneous decode of a DirectPath instruction ezt úgy értelmezem, hogy ez olyan ciklusjellegű és ciklusváltozótól függő utasításoknál, mint a REP MOVSx, leállítja a teljes dekódolást az utasítás lefutásáig. Vajon az olyan x87 utasítások, mint FSIN, FCOS, FPATAN is ilyenek?). Az a kóbor rémképzetem van, hogy a K10-ben tapasztalt szembetűnő VectorPath->DirectPath (Double) áttérés és az x87 háttérbe szorítására figyelmeztetés valamelyik fenti elképzelésem felé visz, és afelé, hogy ''záros határidőn'' belül eltűnik a VectorPath.


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Raymond #504 üzenetére

    Ez a load/store volt eddig az egyik legkérdésesebb számomra, hogy mit lépnek az Intel-hez képest.

    Éppen Socket F-re állok át...

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #506 üzenetére

    Arra tudnál (közelítő) értéket mondani, hogy mennyivel lassul? Nem teljesen titsza, hogy a fenti idézett mondatot hogyan kell értelmezni.
    És ha nem áll le teljesen, akkor főleg az nem tiszta, hogy milyen jellegű microcode fut változó ciklushosszú VectorPath esetében.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #525 üzenetére

    Tökéletes pipe-olt futtatási eredmény. 1-1 2x64 bites adder (ADDPD), szorzó (MULPD) dolgozik. Minden órajelben egy utasítás indul folyamatosan, a 3. vagy 5. órajel után minden órajelben egy vonul vissza, tehát az egész 100000+2 és 100000+4 órajel alatt fut le elméletileg.
    A DIVPD és SQRTPD nem pipe-olt.
    Az XORPD-t nem értem inkább. Nem 2 vagy 3 SSE ALU van a Core2-ben? 2 esetén már 0.5 órajel/utasításnak kellett volna kijönni.

    ''Gondolom az 1,1 órajelből az 0,1 azért jött be mert nem volt egy 9. regiszter a további latency time átlapoláshoz.''
    Nem, a register-rename működik, minden utasítás eredményének új register-be kell kerülnie, a programozói register-készlet nem szűk keresztmetszet.


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #530 üzenetére

    Téged is megzavart, hogy magát a CPU-t és az execution unit-okat is pipe-nak nevezik (én is, de az össes dokumentácó ilyen). Megpróbálom leírni röviden az K7-en levezetve a microarchitecure-t. K7-en, mert alapvetően alig tér el a K8 és a K10-től, és ehhez nagyon részletes dokumentációt adott ki az AMD.

    [kép]
    Maga a teljes CPU egy pipeline, csak 3-way superscalar miatt órajelenként max. 3 ottani adategység léphet tovább a következő stage-re (minden ábrán minden szint között legalább 3 nyíl megy függőlegesen). A lépések, órajelenkénti bontásban:

    [kép]
    cycle 1. FETCH: kiszámolja a következő utasításablak címét (+branch prediction, CALLs, RETs, ...), amit az L1-ből vagy a memóriából be kell tölteni.
    cycle 2. SCAN
    : meghatározza az egyes utasítások elejét és végét, majd vagy a DirectPath-ra (legfejlebb 6 utasítás/órajel) és VectorPath-ra továbbítja őket (legfejlebb 1 utasátás/órajel).
    cycle 3. ALIGN1 (DirectPath): 8-byte-os sorokban kapja az utasításokat, minden sorban legfejlebb 3 utasítás lehet (ekkor még minden 1 byte-os x86 utasítás VectorPath-ra került), 9 ilyen sort pufferel. Minden órajelben legfejlebb 3 utasítást megpróbál továbbítani az ALIGN2-be.
    cycle 3. MECTL (VectorPath): a kapott VectorPath-utasításhoz meghatározza a microcode-ROM belépési címét.
    cycle 4. ALIGN2 (DirectPath): prefix-ek, az opcode-, ModR/M- és SIB-byte-ok megkeresése, és ezen információk továbbítása.
    cycle 4. MEROM (VectorPath): a microcode-ROM-ot megnyitja (indexeli, nem is tudom, hogy mondjam) az adott belépési pontnál.
    cycle 5. EDEC (DirectPath): az ALIGN2-ből és a MEROM lépcsőtől kapott infomációk alapján macro-opokká dekódolja az utasításokat. Ezenkívül meghatározza az azonnal argumentumokat, register-pointereket és eltolásokat.
    cycle 5. MEDEC/MESEQ: közvetlenül a microcode-ROM-ból jönnek a macro-opok (ezek saját különálló belső register-csoporton dolgoznak)
    cycle 6. IDEC/Rename: a macro-opok elosztódnak a két ütemező között. Az összes macro-opnak, ami az FPU-egységbe kerül, a register-argumentumai leképeződnek a belső registerfile egy-egy elemére (itt fordul át pl. az 'XMM1' vagy 'MM6' a megfelelő sorszámú belső register sorszámára - 88- vagy 120-entry register file).

    Innen az összes macro-op a Instruction Control Unit-ba (ICU) kerül. Innentől a pipe egy hurok, tehát tovább innen indulnak a macro-opok, és itt is fejezik be pályafutásukat (visszavonulás/retirement), vagy az machine state-nek (register-ek, flags, ...) eredményük szerinti felújításával, vagy kiléptetéssel (mert téves elágazási ágjóslat volt), illetve itt váltódnak ki a kivételek is. Ez osztja el a macro-opokat az integer és a FPU-egységek felé.

    [kép]
    Az integer egység:
    cycle 7. SHED: az integer ütemező, itt várják meg a macro-opok, hogy megérkezzen az összes input adatuk. (És AMD-nél valóban megvárják, nem úgy, mint Netburst alatt.) Közvetlen kapcsolata van a result bus-sal, így az input adatok nem a véglegesített machine state-ből jönnek, hanem rögtön a kiszámolás után. Innentől az elemi egységek a macro-opok helyett micro-opok, ezekből egyszerre legfejlebb 6 léphet tovább órajelenként, 3-3 az ALU-kba és az AGU-kba.
    cycle 8. EXEC: a micro-opok végrehajtódnak, az eredményüket kiteszik a result bus-ra. Ha egy macro-opban levő minden micro-op lefutott, akkor az ICU elindítja a visszavonulási (retirement) folyamatot.
    cycle 9. ADDGEN, cycle 10. DCACC, cycle 11. RESP: address generation (effektív -> lineáris cím konverzió, az effekív címet számolják az AGU-k), Data Cache access, Response, load/store kapcsolat a Data Cache-sel.

    [kép]
    Az FPU-egység sokkal ''érdekesebb'':
    cycle 7. STKREN: stack rename órajelenként legfeljebb 3 macro-opot fogad az ICU-ból, valamint x87 utasításoknál az ST(x) alakú stack-relatív formát fordítja a megfelelő sorszámú belső register sorszámára (88- vagy 120-entry register file)
    cycle 8. REGREN: register-átnevezés. Minden művelet eredménye fizikailag más register-be íródik (tehát az ADDSS xmm0,xmm2 formában a bemeneti értékek mondjuk az fp15 és fp54 fizikai register-ek, de az eremény NEM az fp15-be fog íródni, hanem egy másik register-be.
    cycle 9. SCHEDW: sheduler write, akkor íródnak a macro-opok az ütemező pufferébe, órajelenként 3.
    cycle 10. SHED: maga az ütemező. Innentől megint a micro-op az elemi egység. Az ütemező folyamatosan scan-neli a puffer-ét, legfejlebb 3 olyan micro-opot keresve, amiknek már nem kell várniuk a bemeneti értékeikre, hogy továbbítsa ezeket az EXECUTION UNIT-ok felé. Minden micro-op csak bizonyos unit-ban indulhat el, néhány micro-op csak ''bizonyos időben'', mind a háromra keres egy-egy ezeknek megfelelőt. Azok még a következő órajelben a cycle 11. FREG stage-ben a register-ekből kiolvassák a bemeneti értékeiket (minden unit saját, belső register-eivel dolgozik, nem közvetlenül a register-file-ba), majd elindul a futtatásuk.

    És ezek az execution unit-ok az FADD, FMUL és az FSTORE/FMISC. Órajelenként egy micro-opot tudnak fogadni, az ábrán megnézve a CPU 15 stage-es pipe-ja itt (megint, mint ahogy a két ütemező felé, vagy a 3 ALU/AGU esetében) elágazik, az FEXEC1-FEXEC4 az 12., 13., 14. és 15. stage-ek vertikálisan, de ezek 3 különálló pipe különböző szintjei. Valójában nem is három, mert egy-egy unit megintcsak többfelé ágazik, csak a port közös bennük. Ráadásul ezeknek az ágaknak nem is mindegyike teljesen pipe-olt (pl. az osztó), és nem is egyforma hosszúak (az MMX ALU csak 2 lépcsős, FEXEC12 és FEXEC 13 szintje van csak), de a leghosszabb ág is csak négy stage hosszú.
    az FADD alágai: 3DNow!/SSE adder, MMX ALU/shifter, FP adder
    az FMUL alágai: 3DNow!/MMX multiplier/reciprocal, MMX ALU, FP multiplier/divider/square root (ez utóbbi úgy pipe-olt, hogy négy lépcső hosszú, de az egyik lépcsőben mondjuk 30 órajelig marad az micro-op számolás közben).
    Említettem, hogy a portok órajelenként tudnak egy micro-opot fogadni, viszont az alágak nem törvényszerűen. Ezért szokták megadni utasításszinten a latency mellett a throughput-ot mint legfontosabb adatot, azaz, hogy ugyanaz az ALÁG hány órajelenként tud új utasítást fogadni. Ha az alág pipe-olt, akkor órajelenként egyet, de ha nem, akkor meg kell várni majdnem a teljes lefutást. Például az lappangása K8-on DIVPD 37 órajel, és a következő DIVPD 34 órajel múlva indulhat el. Viszont ezen 34 órajel alatt az FMUL egység vígan tud fogadni és lefuttatni 34 szorzó micro-opot.
    A másik lényeges dolog ezeknek az egységeknek a szélessége. Ha K8-ról beszélünk, akkor ott egy 64 bites összeadó van az FADD egyégben, ezért egy ADDPD macro-opja két összeadó micro-opot fog tartalmazni, ezek egymás utáni órajelekben lépnek be az FADD potján, egymás után mennek a pipe-stage-eken, a második 1 órajellel később fejeződik be, ezért programozói szinten ADDPD utasítás lappangása 5, átvitele 2. Tegyünk mellé még egy 64 bites összeadót, ekkor az ADDPD-hez csak egy micro-op kell, nem gyorsítunk semmit magukon az elemi összeadókon, és máris az ADDPD lappangása 4, átvitele 1 lesz.

    Remélem, így már tisztább.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #535 üzenetére

    A végéről kezdve:

    Az AGU-k generálják mindig a címet, legyen az load, store vagy load/store (utóbbi csak x86 integer utasításkészletben van). A betöltött adat rákerül a result bus-ra (a legelső képen nem ábrázolják teljesen, de a load/store queue-ból feletti, belőle kiinduló nyilak megfelelnek neki), innen kerül a megfelelő műveletekhez.

    Igen, az ADD reg, mem két micro-opból áll, egy load és egy add. Van még store és load/store micro-op, utóbbi az ADD mem,reg hatékonyságát segíti elő, nem kell a címet kétszer kiszámolni (mint P3-mon, és úgy sejtem, Netburst-on, köszönhetően az Intel teljesen micro-op szintű megközelítésének).

    x-utas csoportasszociatív cache: nem a kérelmek számát jelenti, hanem a cache leképezését a fizikai memóriára.
    Ha teljesen asszociatív a cache, akkor a fizikai memória bármely területe kerülhet bármely cache-vonalra. Hozzáféréskor viszont keresni kell a teljes cache-ben, nagyon lassú, mert az összes vonalat meg kell vizsgálni.
    Ha direct-mapped, akkor a fizikai memória annyi azonos méretű folytonos területre van felosztva, ahány cache-vonal van, és minden területről ugyanarra a cache-vonalra kerül az adat. Nagyon gyors, nem kell keresni, de nem hatékony, gondolom, nem kell mondanom, miért.
    x-utas csoportasszociatív cache: a kettő keveréke. Adott fizikai cím a cache meghatározott x számú vonalának egyikén lehet, csak ezek között kell keresni. Ez általában a fizikai cím legfelső x bit-jének elfelejtésével generálják, ezért a folyamatos memóriaterületek hozzáférése is hatékony marad.

    Raymond: köszönöm, akad itt egy-két egység belőle. :)

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Rive #514 üzenetére

    macro-op cache: nem gondoltam a disszipációra, csak arra, hogy a ciklus-utasítások megspórolnának pár stage-t (gyorsulás). Az x86 beállt olyan 60-120W fogyasztásra, beleférnek növelő lépések, más, csökkentő lépések mellett.

    elágazás-kezelés: úgy gondoltam, hogy csak azon elágazásoknak futna le mindkét ága, amelyek rendelkeznek az előző pontban említett prefix-szel, tehát a fordító jelezné, hogy IF-ELSE-ről van szó, és a displacement 8 bit-es (ezt elírtam), szóval a L1 instruction cache-re sem nehezedne számottevően nagyobb nyomás. Cikluselágazásokra teljesen megfelelő az eddigi becslési módszer. Ilyen vegyes megoldást még nem láttam sehol, és az random adatokon alapuló IF-ELSE-t minden mai x86 architechure is megsínyli. A prefix ötletét meg az Intel branch hint prefix-éből vettem.

    Hyper-Threading: nem hiszem, hogy (prioritással kiegészítve) szélesíteni kellene a jelenlegi architectúrát. A fő cél az execution unit-ok lehető legteljesebb kihasználása, úgy, hogy ne egymás elől vegyék el a sávszélességet, bármilyen áron. Felsoroltam az elgondolásaimat, nem biztos, hogy mindegyik megfér egymás mellett.
    El tudom képzelni, hogy egy high-class szál mondjuk az ő CPU-idejében főszálként fusson, máskor pedig mint másodszál, ami kihasználja a szabad execution portokat és macro-op helyeket. Persze, ez akkor hoz nagy teljesítménynövekedést hogy igaz, amit sejtek, hogy decode után (azaz utasítássorrendben) AMD-nél macroop-hármasoknál nincs vízszintes mozgás, tehát egy IMUL eax,ebx; IMUL ecx,edx; ADD esi,edi két hármasra fordul, ugyanúgy, mint egy ADDSS xmm0,xmm1; ADDSS xmm2,xmm3; MULPS xmm4,xmm5 (The instruction control unit takes the three macro-ops per cycle from the early decoders and places them in a centralized, fixed-issue reorder buffer). Ezesetben a tripletek teli vannak üres helyekkel (bubbles), amit ki lehetne tölteni.

    Az Intel a cache mellett azzal is elszúrta, hogy a HyperThreading-et a PPro óta megjelent ''legkeskenyebb'' micro-architektúráján alkalmazta (4 port, shared INT/FPU).

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #557 üzenetére

    Valóban nem figyeltem, hogy OP reg, mem utasításokat használtál, de ekkor nem a Data Cache a szűk keresztmetszet? Hányszor 128 bites az átvitele órajelenként?
    A ''4/8 bájtos utasítás'' kifejezéseken mit értesz?

    Core/Core2-ról csak nagyon óvatosan merek mondani bármit is, nem ismerem őket eléggé. Tegnap találtam egy nagyon jó dokumentációt róluk, annak áttanulmányozásáig inkább maradok a kérdéseknél velük kapcsolatban. (Most a Raymond által linkelt - köszönet érte - [link] anyagot próbálom összerakni egységes egésszé, picit darabos) . A P2/P3, Netburst és K7 micro-architecture-öket ismerem testközelből alkalmazásprogramozás szinten, mivel (nem klasszikus értelemben, de) irodai programokat készítek, ezekkel találkozom mindenfelé nap, mint nap, és nekem is ezek voltak eddig. A K8-on sem dolgoztam még, de megpróbálok képben lenni vele kapcsolatban. Remélhetőleg a dual K8 konfigom összeáll júniusra, de már ezt direkt úgy terveztem meg, hogy (a lehető legkisebb módosításokkal) K10 fogadására is alkalmas legyen.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #559 üzenetére

    Egy kicsit elmélyedtem a Core2 micro-architecture-ben, egyáltalán nem érzem rossz döntésnek, hogy az AMD ermékvonalán maradok egyelőre. Ezzel a kóddal elég sok szűk keresztmetszetet megtaláltál, de pont nem azt, amit írsz.

    - Igaz, hogy négy decoder van, de csak az első tud több, mint egy micro-opból álló utasításokat fordítani, tehát 4-1-1-1 micro-opre forduló utasításszekcenviák tudják teljesen kihasználni a teljes decode-sávszélességet (...óhh, azok a boldog P2/P3 idők, csak ott még 3-1-1 volt a felállás. Minden OP reg, mem 2 micro-op-ra fordul le (op+load), szóval órajelenként csak egy XORPD reg,mem fordult le egyáltalán. Legalább a trace cache-t megtarthatták volna...

    - minden load micro-op a LOAD (port2) exetucion unit-ba kerül, órajelenként egyet tud fogadni. A Data Cache is egy olvasás/órajel szélességű, szóval ha más miatt nem, akkor emiatt is órajelenként egy XORPD reg,mem indulhatna el, egy kapja meg a forrásadatát per cycle. (K8/K10-en 3 AGU van, és a cache 2 load/cycle szélességű). A Data Cache és az core között nincs más ideiglenes tár a Store Buffer-en kívül - az már L0 lenne -, tehát ha még ugyanazt az egyetlen értéket is olvasod be minden utasításnál, akkor is a cache-hez kell fordulni mindig. A Store Buffer meg a store-forwarding-ot tudja segíteni, a kódban viszont nincs store.

    A 0.33 utasítás/cycle legfeljebb úgy érhető el, hogy XORPD reg1,reg2 alakokat használsz (úgy, hogy nincsenek függőségek, és reg1 != reg2, mert erre spec. gyorsítás van).

    Azt hittem, register-es címzést használsz, XORPD xmm0,xmm1 ugyanakkora méretű, mint az XORPD xmm0,[esi] és a XORPD xmm0,[esi+10h] is csak egy byte-tal nagyobb, +/- 127 byte-os displacement-ig. De pont a 4-1-1-1 felállás miatt itt mindegy, hogy egy 16 byte-os sorba 2, 3 vagy 4 utasítás fér el.

    [mod]: Dzsémi, ne rajtam hozzászólás-gyűjtögess! :D

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Rive #544 üzenetére

    Én programozói szempontból tudok csak kiindulni, csak azokat tudom, hogy mik segítenék a munkámat.
    Ennél a kódnál [link] (SSE IDCT, 2x4 oszlopot konvertál egymás után, majd 8x1 sort SSE2 integer megvalósítással gyorsabb lenne, de mindenképpen a lehető legpontosabb eredmény kellett itt) ha lenne egy megfelelő, shared L1 Data Cache-en alapuló Hyper-Threading, akkor párhuzamosan mehetne a 4 oszlopok dekódolása, nem kellene egymás után írni őket, a függőségek miatt úgyis ''lassú'' a végrehajtás, és kevés egység dolgozik egyszerre, akármennyire is szét vannak dobálva a függő utasítások. Shared L1, mert a cache-vonalak átvitele két cache között nem túl gyors művelet.

    Ennél a kódnál [link] pedig annyira véletlenszerű a forrásadat, hogy biztos vagyok benne, hogy nagyon sok a misprediction, ezen segíthetne, ha mindig mindkét ág elindulna. (Ugyancsak a függőségek miatt mindig van szabad ALU).

    Mindkét kód saját készítés, kéretik bárkinek felhasználás előtt kikérni az engedélyem :)
    26 megapixeles képen a fenti két kód lefutása 2400 MHz-es K7-en (gettickcount-tal mérve):
    - IDCT: több, mint félmillió teljes lefutás kb. 200 millisec alatt
    - Huffman-decode: több, mint 17 millió teljes lefutás: 650 millisec alatt
    Nagyon kíváncsi leszek, mennyivel gyorsul majd K8-on.


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Raymond #565 üzenetére

    Csúsztatások egy bizonyos szint alatt nem szoktak lenni, csak mese, mese vég nélkül (mint az intelligens mosópor... legiknkább a könnyebb megérthetőség miatt, mondjuk a CPU azért elég intelligens dolog) Sokszor jó, ha az ember megpróbálja lehozni ezeket a meséket is tranzisztor-szintre. :)

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Raymond #610 üzenetére

    Nagyon jó cikk, minimális háttérismeretek birtokában érthető (= ha tudja az ember, hogy miről beszél, akkor tudja azt is, hogy mit mond). Egy kicsit zavaró, hogy micro-opokban fogalmaz macro-opok helyett.

    dezz #617: K10 esetében is van result bus (írja is, hogy nem ábrázolja, mert túl bonyolult lenne az ábra tőle), result bus nélkül az egész működésképtelen lenne (INT oldalon nincs is register-rename, csak resultbus->ROB/ICU forwarding, legalább K7 óta). :)

    dezz #619: Sajnos az Intel visszatért a PPRO/P2/P3 complex-simple-simple... (itt most még egy -simple, 4-1-1-1 micro-op) decoder felállásához. Valahogy mindig RISC-felfogású decoder-eket akarnak tenni egy x86 elejére P6 óta (Netburst alatt az egyszem decoder szűkösségét elnyomta a trace-cache), pedig pont oda kellene CISC-felfogás. Ezek szerint három egymást követő OP reg,mem típusú utasítást Core2 3, K10 1, (de még ha nem DirectPath Double útra mennének, akkor is) legrosszabb esetben 2 órajel alatt fordítaná. Itt is van egy szűk keresztmetszete, illetve a mem tagokat Core2 1/órajel (cache lehet, hogy dual-ported, de LOAD - port2- csak egy van), K10 2/órajel szélességben hozza fel Data Cache-ből legjobb esetben, akár 2x128 bitet.

    akosf #619: üresben ugyan nem járnak, de nem biztos, hogy FP munkát végeznek, lévén shared INT/FPU pipe-ok (számláló, forrás- és célcímek kezelése, branch futtatása, stb...)
    Pont a macro-op cache-t említettem korábban is, a fetch/decode itt majd' a pipe felét viszi el, nem csak 2-3 órajelet/stage-t. A másik ötletem, a Hyper-Threading azért játszhatna itt, mert mostmár teljesen tiszta, hogy a pack-stage-ek után nincs vízszintes mozgás a macro-ophármasokban, minden utasítás arra a portra megy, amelyik lane-en van, főleg FPU-ban teli van gap-ekkel. Ezeket tölthetné meg másik szál, INT esetén pedig az 3 micro-op/cycle miatt nem nehezedne nagyobb nyomás (INT-ben sokkal nagyobb gondot okoznak a függőségek, mint a szélesség). A branch-ágak práhuzamos futtatása sajnos nem, megintcsak amiatt, mert nincs INT oldalon register-rename.

    dezz #622: 3+2+2+2-t nem tudok kiolvasni az Optimization Guide-ból, csak 3/2+2+2-t. Mivel végig 3-széles maradt a micro-architecture (3 macro-op/sor), szerintem nincs 4 decoder, a VectorPath csak ábrázolásszinten külön oszlop (vagy a 3 DirectPath, vagy VectorPath).

    akosf #623: érdekes módon az FSTORE-t az idők folyamán átnevezték FMISC-re. Mindenesetre ennek van belső ROM-táblája a betölthető konstansokhoz, ez kezeli az INT->FP és FP->INT konverziókat (fild, fist(p)), és ahogy mondod, az FSIN, FCOS, ... utasítások által generált spec. micro-opokat futtatja (lehet, hogy csak ez fér hozzá a belső, programozói szinten nem látható átmeneti (scratchpad) register-ekhez?)
    IPC: majdnem mondtam, hogy próbálj egy átlapolt, nem függő FPU/INT utasítássort, de azt meg a decode limitálja 3-ra.

    dezz #624: azért majd meglátjuk, 65 nm-en meddig fogják felvinni az órajelet, mennyi tartalék van benne.

    akosf #625: felfüggesztődik? Nem hiszem el...
    SMC: egy (a? :) ) másik topikban zajló események miatt pár napja multi-core/multi-processor/NUMA témába ástam bele magam. SMC természetesen lehetséges (még mindig 8086 miatt, de már nem sokáig lesz ez így), AMD-nél a MOESI-szerinti cache-probe módosítás esetén kiüríti az I-cache adott vonalát (store-jellegű probe esetén az I-cache-t is ellenőrzi system-wide), és érvényteleníti a teljes CPU-pipe-ot. Ha jól emlékszem, Netburst alatt nyomta ki a teljes instruction cache-t egy SMC.

    dezz #628: DirectPath-on mennek egy 1 micro-opos utasítások (3 micro-op/órajel), DirectPath Double-re a 2 micro-oposok (1.5 micro-op/órajel, lényegében ugyanaz, mint a DirectPath Single, csak 2 órajel alatt fordít), VectorPath-ra a többi. Csak ennyi a szabály.

    akosf #629: OP reg,mem már 2 micro-op Intel-nél. Most hivatkoznék megint a fenti IDCT-kódomra, sokszor elemi utasításoknál sem férnek el a konstans-ok a 8(/16) XMM-registerbe.
    Igen, kb. annak felel meg, aminek leírtad. Csak AMD esetében van DirectPath Double, és sokkal több minden fér bele valamelyik DirectPath-ba, K10 esetén meg már főleg).

    Első gondolatom a cikk után az volt, hogy vagy nagyon rááltak a server-piacra, vagy hamarosan több CPU-s rendszereket akarnak látni az otthonokban.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Raymond #633 üzenetére

    Nagyon egy dologra gondoltunk. :) +[link]

    Nemrég néztem, egx 2000 CPU ára 2-30 dollár környékén van, egy 8xxx CPU-é $2000 felett...

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz P.H. #634 üzenetére

    Persze elrontottam, szóval a 2xxx CPU-k 2-300 dollárnál kezdődnek, 8xxx CPU-k pedig jóval $1000 felett. Amellett, hogy 8xxx 4/8-CPU-s rendszerekhez kell, vajon mennyivel nagyobb költség a megteremtésük (főleg a kimondottan egységesített gyártásfolyamat óta), mint egy asztali X2-nek? :)

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #636 üzenetére

    Egyik SSE utasításkészletben nincs OP mem,reg forma, x87 szinten sincs (csak az x86 orthogonális utasításaiban) csakis OP reg,mem, OP reg,reg és store alak (az MMX/SSEx csoportokat már igencsak RISC-szemléletben tervezték).. x86 szinten pedig csak egy store lehet/macro-op (32/64 bit), az OP mem,reg formára pedig van egy spec. load/store micro-opuk, ami csak egyszer számolja a címet. és megvárja a művelet visszavonulási eredményét.
    Mindez szemben az Intel load+op+store_address+store_value (4 uop) megközelítésével, bár Core/Core2 esetén már a load+store_address uop-fusion-nel van egyesítve.)

    [mod] Raymond: Nem olcsóak, de megdöbbentőek a különbségek, hogy 4/8 socket-es rendszereket mennyivel drágább összeállítani, mint 2-socketes rendszereket. Az igényeket, de főleg a skálázhatóságot igencsak meg kell fizetni.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #639 üzenetére

    mod: nem, 3 macro-op széles a DirectPach (Double) és a VectorPath is (csak a DirectPah után van register-fordítás, a VectorPath-os micro-opok mindig belső registereken dolgoznak), ez 6 micro-op (1-1-1 op+load, op+load/store, store (idáig biztos) vagy spec - esetleg spec+spec)) lehet, úgy tudom. Írtam, hogy Ő furcsán micro-opot írt mindig.

    ''Hogy jön össze K10-en 3-nál több?''
    pl. FSIN, FCOS, és a többi rögzített ciklusszámú utasítás (a cikk írja, hogy régen az integer osztás is ilyen volt), ezek sokkal több, mint 3 macro-opot adnak.

    ''Mondjuk DirectPath esetén is ennek kell történnie, mert 2 uOP a sima dekóderek kimeneti szélessége. Nem?''
    A DirectPath decoderek kimeneti szélessége 1 (DirectPath) vagy 2 (DirectPath Double), de azt hiszem, ezért van 2 Pack stage utánuk, ezek egyesítik és osztják el végül a 3-3 lane-re a marco-opokat.

    1 IPC alá egyik x86 micro-architecture-n sem eshetne, erre kell, hogy tervezzék a scheduler pufferének (schceduler-ek puffereinek) nagyságát. De ha egyszer csak minden utasítás egy előző (nagyon hosszú) lefutású, mondjuk FSIN-től függ, akkor látszólag megállhat az élet.

    ''Tényleg, az x87-es utasításokat tudják kezelni a sima dekóderek - annak ellenére, hogy adott esetben ugyanarra a uOP-ra fordulnak?)'' Meg kell nézni a fent linkelt Software Optimization Guide-ot, Appendix C Instruction Latencies :) De nagy részét igen.

    ''Lásd Athlon FX, és később Phenom FX vonal (socket F esetén): 2-procis rendszerek gamingre, videofeldolgozásra, stb.'' Ehhez le kell vinni az árakat olyan szintre, amit meg is fizetnek érte az egyszeri, de nem tapasztalatlan vevők. Nekem úgy néz ki, hogy a négymagos Socket-F rendszerem összeáll egy Core2-rendszer ára alatt, és ha tényleg működik, akkor is igen-igen nagy szerencsém van megint.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #641 üzenetére

    Megpróbálom leírni, az integer pipe-on keresztül, hogy mi is történik. Először is a kétszintű fordítás alapegységei:
    - az x86/x87/MMX/SSEx utasítások változó hosszúak (ízig-vérig CISC), szinte semmi szabályosság nincs bennük (ott van, ahol legalább ModR/M byte van), lehetnek ''egyszerű'' store, ''egyszerű'' load, vagy összetett load+operation illetve load+operation+store műveletek.
    - macro-op: rögzített hosszúságú, regularized instruction field jellegű egységek, (ezek RISC jellegre utalnak, de még komplexek, mivel) általában egy tartalmaznak egy operation jellegű műveletet és tartalmazhatnak egyet a következők közül: load, store, vagy load/store azonos címre, ez így összesen adja ki a legfejlebb 2 micro-opot.
    - micro-op: rögzített hosszúságú, regularized instruction field jellegű egységek, egy primitív RISC műveletet jelentenek, pontosan egyet a következők közül: load, store, operation.
    Mindkét esetben az operation jelenthet integer vagy floating-point műveletet. (A K10 dokumentáció is ezen alapegységek definiálásával kezdődik).

    A CPU pipeline egy jelentős része (az Instruction Control Unit-ig) azzal van elfoglalva, hogy a CISC-utasításokat macro-opokká konvertálja.
    Pontos adatot arra, hogy egy macro-op mekkora méretű, nem találtam, de ezek a mezők vannak benne biztosan (az elnevezéseket a micro-opon kívül én találtam ki, egységes nevet nem tudok, illetve így egybegyűjtve nem találtam meg sehol a macro-op mezőit):
    [res | resflag | resenv | micro-op1 | micro-op2 | tag | #chn]
    Ezek ilyen hármas sorokban vannak rendezve, ezen hármasok egy elemi egységet alkotnak.
    - res: ha a macro-op módosít egy register-t, akkor melyiket, és mivel (maga az eredmény).
    - resflag: ha flag-jellegű eredménye is van, akkor micsoda (flag-jellegű, mert más lehet integer esetben - EFLAGS -, más FP-esetben - status word)
    - resenv: a kiváltható kivételek (branch misprediction, page fault, divisionbyzero, ...) egy-egy jelzőbitje
    - uop1, uop2: azon legfejlebb 2 micro-op (op, load, store, load/store), amiből állhat egy macro-op. Ahogy vannak, teljes egészükben. (dezz: ezen fadd32 micro-op azonos ADDSS és FADD esetén, de a macro-op környezet teljesen más).
    - tag, #chn: minden macro-op kap egy sorszámot 0..23 között (23->0 átfordulásnál van jelentősége egy wrap-bitnek, ami jelzi az átfordulást), a #chn azt jelenti, hogy a hármasban melyik pozíción van (0..2). Éppen 24 db hármas macro-op sor van a ICU-ban, tehát egy-egy macro-opot pontosan azonosít egy ilyen #chn+tag-pár a teljes CPU-ban.
    Ne feledjük, hogy az ICU-ig pontosan programsorrendben vannak az macro-opok, ennek a register-átnevezésnél és a függőségek feloldásánál igen fontos szerepe van. Valamint a macro-opok látható nagy méretéből következően sejthető, hogy a macro-opok fizikailag nem továbbítódnak az ICU-ból, megülnek benne, csak a ''címük'' továbbítódik a következő stage-ek felé. Egyrészt lenne ilyen fizikai továbbítás, akkor az akár negyed/fél kilobyte-os nagyságrendű adattotvábbítást jelentene a stage-ek között, ez felesleges pazarlás lenne. Másrészt a visszavonulási folyamatban vissza kell állítani az program szerinti utasítássorrendben, ami ezáltal felesleges, mert adott.

    A fordítás során először meg kell határozni az utasítások elejét és végét. Adott méretű sorokban kerülnek az ezt végző egységhez az utasítások, ismert belépési ponttal (klasszikus értelemben ez egy Instruction Pointer), tehát az első utasítás eleje ismert. Ha az adott sorral akkor találkozik először az egység (most került be a memóriából az I-cache-be), akkor órajelenként 1 utasítás hosszát tudja meghatározni, majd lépteti a belépési pontot. De ha nem, akkor órajelenként több utasítás mehet a következő lépésbe, az is adott, hogy melyik (DirectPath vagy VectorPath) ágra. Miért?
    Ha egy cache x KB méretű adatot tartalmazhat egyszerre, NEM azt jelenti, hogy pontosan x KB méretű. Minden cache-vonal mellett további információkat tárol, például az ECC vagy parity biteket. Ezek mellé még fel lehet venni pár speciális információt tartalmazó bitet. Az utasításhossz-meghatározó egység pl. egy-egy ilyen spec. bitbe vissza is írja az utasítások elejének és végének jelzését a vonal mellé, tehát ha legközelebb találkozik ugyanezzel a vonallal (pl. ciklus), akkor már nem kell dolgoznia (ezek a bit-értékek L1->L2 transfer után is megmaradnak, tehát az L2 is tartalmazza őket).
    Az utasítás végét egy egyszerű ok miatt jelzi (tudható lenne a következő utasítás elejének jelzéséből), a kezdő- és végzőbit bitkombinációi azt is tárolják, hogy az utasítás milyen decoder-ágra kerül. Ez egyszerű ökölszabályokkal leírható, de eltérőek K7, K8 és K10 esetén, mondjuk az ADDPS xmm0,[esi] utasításnál:
    - K7 esetén minden utasítás, ami egy macro-opot generál, DirectPath-ra, a többi VectorPath-ra kerül. A 4x32 bit összeadás 2 db 2x32 bites operation-nal végződik, egy macro-op egy operation micro-opot tartalmazhat, tehát VectorPath lesz (a 2 load itt nem játszik, mert elfér az egy-egy operation mellett).
    - K8 esetén bekerült a DirectPath Double szemantika, 1 macro-op (általában) DirectPath-ot, 2 macro-op (általában, jellemzően FPU esetén) DirectPath Double-t, az összes többi (akár egy macro-opos utasítás is lehet) VectorPath-ra kerül. 4x32 egyanúgy 2 db 2x32 operation, tehát DirectPath Double. De mondjuk a PUSH eax (ami egyenértékű SUB esp,04h + MOV [esp],eax műveletekkel) 2 macro-op, de VectorPath-ra kerül.
    - K10 esetén 1 macro-op DirectPath, 2 macro-op DirectPath Double, 3 vagy több pedig VectorPath-ot jelent. ADDPS valóban egy darab 4x32 bites operation, tehát DirectPath. (PUSH eax még mindig 2 macro-op, szóval DirectPath Double).

    K7 és K8 esetén van egy speciális átkapcsolás a DirectPath és VectorPath között, mert az MROM-ból kétféle macro-op jöhet (K7-nél a cycle 5: EDEC lépcsőben, ahogy fentebb írtam).
    - macro-op sablonok, az említett PUSH eax esetében SUB ESP,04h és MOV [esp],xxx. A DirectPath utolsó lépése behelyettesíti az xxx helyére itt az eax register nevét. Általánosságban, minden VectorPath-on induló, de (akár csak a címben is) register-argumentumokkal rendelkező utasítás esetében ez történik.
    - a komplex, nagyon sok macro-opra forduló műveletek (FSINCOS, vagy IDIV) a register-készlet kiterjesztéseként felfogható, kívülről nem látható registerkészleten dolgozik. Minden macro-opnak van eredménye, ami register-be íródik, kelletlen lenne, ha mondjuk az FSINCOS az átmeneti értékeit a x87-stack-ba pakolná, és csak úgy lehetne lefuttatni, ha mondjuk legalább 6 üres register van a stack-ban. Ezekbe a macro-opokba bele van drótozva, hogy adott macro-op melyik registerbe tegye az éppen kiszámolt átmeneti értékét.
    K10 esetén már csak az utóbbi macro-opok jönnek az MROM-ból, ezért ábrázolnak 4 decoder-t egymás mellett, viszont a VectorPath mellett nem fordulhat DirectPath-on egyidejűleg más.

    Eddig megkaptuk a macro-opokat, ezeket hármasokba kell rendezni. Integer oldalon nincs nagy gond, mert minden ALU-ban és AGU-ban bármilyen jellegű művelet végrehajtódhat (''Each of the three ALUs performs general purpose logic functions, arithmetic functions, conditional functions, divide step functions, status flag multiplexing, and branch resolutions. The AGUs calculate the logical addresses for loads, stores, and LEAs.''), kivéve integer szorzás. Nagyobb gondot jelentenek az FPU-ba kerülő műveletek, mert ott minden végrehajtó egység csak bizonyos műveleteket fogadhat, ezután az állomás után viszont a hármasokban nincs cserélődhetnek az elemek, tehát itt ki kell alakuljon az macro-opsorrent a hármason belül, ami meghatározza, melyik egységbe (3 ALU, 3AGU, FADD, FMUL, FMISC) kerül az utasítás. Nem tiszta viszont, hogy pl. ADDPS reg,[mem] esetben az összeadó operation-ök csak az FADD pipe-ba kerülhetnek (0. pozíció a hármasban), akkor macro-opban mellette levő a load-nak is kötelezően az 0. AGU-ban kell-e lefutnia, vagy ezek még itt átrendezhetőek, különválaszthatóak-e. Másik kérdés: mivel az FPU-macro-opok rögzített helyre kerülnek a hármasban, a hármasok teli vannak üres helyekkel, ezek az üres helyek vajon kitölthetőek-e ALU-jellegű macro-opokkal (számláló, prefetch, ciklus kezelése megszokott az FPU-jellegű műveletekkel keverve), vagy azoknak új hármas sor indul (ez utóbbi az ideális utasítássorrendet befolyásolja nagyon kódolás/fordítás közben). Erre gondolok, hogy egy FADD ST(2),ST; FMUL ST,ST(1); SUB ecx,01h (a x87-es utasítások rögzített hármason belüli pozíciója mellett) vajon egyetlen macro-op-hármassá fordul-e ([fadd][fmul][sub]), vagy kettővé ([fadd][fmul][---] és [sub][---][---]). Ugyancsak kérdés, hogy ha csak egyetlen hármasra fordul, akkor úgy is igaz-e ez, ha a SUB az első utasítás (én így szoktam írni a kódokat, a cilkusváltozó módosítása mindig a lehető legmesszebb van a hozzá tartozó Jcc-től).

    Ott tartunk, hogy adottak a macro-op hármasok, ezek programsorrendben bekerülnek az Instruction Control Unit-ba (ezentúl csak az integer oldalt nézem), két dolog van még hátra, a register-átnevezés, és a függőségek feloldása. Ehhez minden macro-op megkapja a hármasban a bekerüléskor a tag (mindig eggyel nagyobbat, mint amit az előző hármaséi kaptak, egy hármasban ugyanazt) és #chn mezőit (pozíció a hármasban). Ez a kulcsa az egésznek.
    Az integer-egység nagyon pörög (emléleti 3+3 művelet órajelenként), nem igazán előnyös klasszikus register-allocation table használata (FPU-ban viszont van), mert akkor a belső register-ek száma irdatlan nagy kellene, hogy legyen. Ehelyett 2 db 16 elemű (az átmenetieredmény-register-eket most hagyjuk figyelmen kívül az egyszerűség kedvéért) register file van (16 elemű: EAX, EDX, ECX, EBX, ESI, EDI, EBP, ESP + a 64 biten megjelent 8 új integer register), a Register File és a Integer Future File. Mindkettő elemei mellett vagy ugyancsak egy-egy tag, #chn, és egy valid érték.
    - az ICU látja, hogy mely register-ekből fogják kapni bemeneti értékeiket a macro-opokban levő micro-opok (akár adat, akár címzés), és a micro-opokba beleírja az Integer Future File-ban adott register mellett levő tag és #chn értéket (= programsorrendben mely művelet fogja módosítani utoljára az adott register-t, mely eredményre kell várni). Illetve, ha a valid bit is be van állítva a bejegyzés mellett, az azt jelenti, hogy az a művelet már le is futott, közvetlenül átveszi az eredményt is, nem kell várni erre az eredményre.
    - azt is tudja, hogy a macro-op mely register-t fogja módosítani (ha módosít egyáltalán), annak tag és #chn értékét beírja az Integer Future File megfelelő bejegyzése mellé, és törli a valid bitet (ezzel mostmár ez a macro-op ''birtokolja'' az adott register-t).

    Így fel is épült a függőségi viszony a műveletek között, gyakorlatilag 0 órajel alatt. És a másik, a Register File? Ahhoz a futási hurok másik végére, a visszavonulási eljárásra kell nézni. Minden macro-op tudja, hogy mely register-be és mit írt, a visszavonulás folyamán az ICU ezen információk alapján mindig naprakészen tartja programsorrendben a Register File-t (nicsak, itt egy másik Instruction Pointer), ugyanúgy mellette a tag és a #chn értékekkel. Ez egyedül azért kell, mert ha pl. kivétel vagy hibás elágazásbecslés történt, akkor egyszerűen a Register File tartalmával egészében felülírja az Integer Future File-t (mellette beállítja annak valid bitjeit is), és az utolsó helyes környezetben folytatódik tovább a futtatás. Tehát a két register file miatt teljesen párhuzamosan folyik a macro-opok fogadás/továbbítása az ütemező felé, és a visszavonulás (órajelenként egyaránt 3-3 macro-op).

    Ezután a macro-opokat eltördeli (breaks down) az ICU a bennük levő micro-opokra, és azok beléphetnek a 3 ütemezőbe. Az, hogy melyikbe, az a hármason belüli pozíción múlik, out-of-order végrehajtás csak az egyes ütemezőkön belül függőlegesen lehet. Mindegyik ütemező után jön egy ALU+AGU (AGU-k végén a közös load/store queue), amelynek a végén egy-egy (azaz összesen három) result bus lóg. Ha egy micro-opnak megvan már az összes bemeneti eredménye, ''szaladhat'' az exection unit-ja felé. Ha viszont nincs, várnia kell a bemeneti értékére, ami meg fog jelenni a result bus-ok valamelyikék. A micro-op kapott egy tag-et és egy #chn-t, hogy mit figyeljen, és melyik bus-on (a #chn miatt csak egy bus-t kell figyelnie). Ha ezt megtalálja, akkor közvetlenül onnan leemeli az eredményt, majd várja a további ismeretlen bemeneti értékeit, ha van még.
    1-1 result bus-on órajelenként 1 eredmény jelenhet meg elméletileg, ezeket az ICU is látja. Minden eredmény mellett ott utazik a tag is (a #chn adott), ezeket összeveti az Integer Future File megfelelő bejegyzéseivel. Ha azok mellett még mindig az a tag és #chn van, ami a result bus-on megjelent (azóta nem módosította semmi az adott register-t, az adott művelet még mindig „birtokolja” azt), akkor beírja az eredményt, és beállítja a valid bit-et. Ha nem azt a #chn+tag-et látja ott, akkor nem foglalkozik vele, a megfelelő micro-op már úgyis látta és megkapta az eredményét.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #645 üzenetére

    Azért írtam le ilyen részletesen, mert korábban felmerült (talán) akosf részéről egy decode elejére vonatkozó kérdés, hogy hogyan lehet egy órajel alatt több utasítás elejét/végét meghatározni. Ennyi az, amit (több forrásból) össze tudok rakni tényeken alapulóan magamban egységes egésszé, de nekem is van kérdésem és sejtésem elég. Sajnos ezen kérdések és sejtések jó része csak az MROM tartalmának ismeretében lenne megválaszolható, de ez meg például a legendává vált (időben kb. még 486-tal párhuzamosan elhelyezhető) Intel-AMD-botrány miatt nem lehetséges, ez a rész már nem publikus. :)

    ''A sima (DirectPath) dekóderek kimenete 2 mikro-op széles. (Vagy itt is 2 makro-opot kellett volna írniuk esetleg?) Naszóval, ebbe beleértendő mindaz, amit írtál még az 1-2 mikro-op mellé?''
    A DirecPath kimenete összesen 6 micro-op - 3 op + 3 load vagy store vagy load/store. Hogy ez 3 macro-op, vagy a 6 micro-op csak később rendeződik 3 macro-oppá, ez kérdés (fel is tettem a hsz-ben). Mindenesetre egyrészt K7 óta az ábrákon a három macro-op az ICU előtt közvetlenül jelenik meg egységszinten, másrészt egyes helyeken a K8-nál pont ezt a szintet (és az átrendezés - részleges - hiányát) jelölik szűk keresztmetszetként.

    pl. sqrt, több makro-opra fordulnak
    K7-nél is már a DIVSS és SQRTSS DirectPath, a DISPS és SQRTPS VectorPath, tehát pontosan 1 és 2 macro-op-ra fordulnak, amik megrekednek hosszabb időre az FMUL pipe egy alágának valamely szintjén.

    CPI (Clock Per Intruction) így persze lehet kevesebb elméletileg, integer úton jó rendezéssel 3 IPC (és így 0.33 CPI elérhető), ha reg,reg formáról van szó. 3 felett nem hiszem, mert a retirement unit 3 macro-op/cycle (és ezt is említik, mint bottleneck-et).

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #648 üzenetére

    ''Ha egy makro-opban az 1-2 mikro-op mellett az eredeti gépi kódú utasításra vonatkozó, ill. abból származó információk is vannak, akkor hogyan lenne lehetséges, hogy először csak a mikro-opok adódnak tovább, és csak később csatolódnak hozzájuk ezek az egyéb mezők? Mindez az információ megvan az egyes mikro-opokban is, tehát a makro-opokba rendezés végülis egy protokoll, ami arra kell, hogy segítse a reorderinget?''
    Affene, hogy mennyire igazad van! Nem lehet különválasztani a memóriához hozzáférő utastásokat az eredeti környezetüktől, legalábbis azért, mert a page fault elég gyakori, ekkor egy kivételt/(software-es) megszakítást kell kiváltani, ami beolvassa a swap-ból az adott lapot, majd az kiváltó thread újraindul a page fault-ot kiváltó utasítástól. Ezt ki lehet pipálni, igen élesszemű meglátás!

    ''Az ICU gondolom a ROB. Mit rövidítesz ICU-nak?)''
    Az Instruction Control Unit-ot rövidítem ICU-nak. ROB (Re-Order Buffer) Intel szóhasználatában létezett először PRro esetén, én pedig idegenkedek az alternatív elnevezésektől - konzervatív vagyok ezen a téren eléggé, x86-64-et is szívesebben használok, mint x64-et -, jelentsék akár ugyanazt. De ROB=ICU, igen.

    ''Az ábrákon a Pack Buffer kimenetét 3 uOP-nak jelölik - az merült fel bennem, hogy ennek inkább 3 makro-opnak kellene lennie, nem? Nekem így lenne logikus. Máskülönben csak 1 uOP-os DirectPath (tehát nem Double) műveletekből jöhetne össze 3.0-át közelítő IPC, Double-ekből már csak max. 1, stb.''
    3 macro-op a kimenete, igen: ''Early decoding produces three macro-ops per cycle from either path. The outputs of both decoders are multiplexed together and passed to the next stage in the pipeline, the instruction control unit.'' (megintcsak a Software Optimization Guide for AMD 10h Processors-ból) Nem egyszerű átlátni, hogy pre-decode, early decode, decode hogy és hol is van, én is keverem néha sajnos.
    A fordítás ICU-ig elég sötét ló, sehol nem találni teljes információt/levezetést róla (inkább csak egymásnak ellentmondóakat, és sajnos utoljára K7-re vonatkozóan találtam AMD által kiadott részletes, cycle-to-cycle leírást még anno két éve). Pedig ahogy kinéz, programozói szempontból ez a leglényegesebb. Ha a fenti kérdést lezártuk, akkor elsődlegessé a következő lép elő:
    FMUL ST(2),ST
    FADD ST,ST(1)

    Ez vajon [fadd][fmul][---]-ra vagy [---][fmul][---], majd [fadd][---][---]-ra fordul? És ha az előbbire (jobb lenne), akkor mi tartja (tarthatja) fenn ott az eredeti programsorrendet? Erre sehol nem találtam információt. Mert fordított sorrendben:
    FADD ST,ST(1)
    FMUL ST(2),ST

    a balról jobbra sorrend lehet vezető szabály: [fadd][fmul][---]

    raymond #647:''If a branch is made to an address which does not have its pre-decode start bit set then we know that something is wrong.'' Erre értettem, hogy még nem találkozott az adott vonallal.
    The instruction pipeline may invoke the pre-decoding hardware in this case to initialize or correct the pre-decoding bits within only two cycles. ... invoke the pre-decoding hardware[...] within only two cycles, de vajon az mennyi idő alatt, és hogyan végzi el a dolgát?

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Raymond #651 üzenetére

    Hogyan tudod párhuzamosítani több utasítás elejének és végének meghatározását, ha változó hosszú utasításokról van szó? Meg kell taláni az opcode byte-ot (byte-okat, mert 486/MMX/SSEx esetén a 0Fh prefix is ott van, 3DNow4 esetén kétszer is - 0F0Fop ), - és még szó sem esett még az REPZ, REPNZ, argumentum- , vagy címhossz-befolyásoló prefixek-ről, amik SSEx - x >=2 - esetében válnak fontos tényezővé- , a ModR/M által leírt címhez tartozó azonnali érték, a SIB-byte-ot, és az adathoz tartozó azonnal érték hosszát. (és ezek is 1/2/4/8 byte hosszúak, prefixről változóan).

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #654 üzenetére

    A 8/16/32/64 bites x86 készlet (folyamatosan bővítve a P1-ig) így néz ki:

    - othogonális stílusú utasítások: op dest,src forma, ahol dest és src vagy register, vagy cím, vagy közvetlen adat lehet, de nem lehet op mem,mem forma, értelemszerűen a dest nem lehet közvetlen.
    prefix | opcode | ModR/M(+SIB) | addr_offset | immediate (32 bitben a leghosszabb utasítás tizenvalahány byte-os lehet).
    pl. lock add [edi+ecx*2+0000100h],eax vagy add eax,20h
    Az kód alap cím- és adatméretet (default word = 16/32/64 bit, 8 bites adatméret mindenhol van, az más opcode) a kódszegmens bizonyos bitjei határozzák meg, ezért az addr_offset és az immediate egyenként 16/32/64 bites lehet. Lehetne, de ha valamelyik -128 és 127 között van, akkor az 8 biten kódolódik, decode idején előjelhelyesen kiegészítődik.
    A default word méretek között egy-egy utasítás erejéig a 66h (adatméret) és 67h (címméret) prefixekkel lehet váltani, 64->16 és 16->64 váltás nincs. Az opcode kódolja az adatméretet (8 vagy default word), a ModR/M és a 32 biten megjelent opcionális SIB (scale-index-base) kódolja az memóriaoperandus címének formáját és hosszát (8 vagy default word méret), ha van egyáltalán, illetve az egyik vagy mindkét register-operandust.
    - van egy nagycsomó egybyte-os egyoperandusú utasítás, közvetlenül opcode-ban kódolt argumentummal: nyolc külön darab INC, DEC, PUSH, POP,... utasítás van a 8 register-re, persze ezeknek van egy-egy 9. formája is, ami memóriaoperanduson dolgozik (ModR/M vagy SIB segítségével), minden 8 bit displacement-es feltételes urgásra (később 0Fh prefix-szel kiegészítve jelentek csak meg a default word displacement-es változatok).
    - a 8086 átvette a 8085 jórészt klasszikusan akkumulátoros/célregister-es utasításait is, ezek is egybyte-os utasítások, pl. a MOVSD 32 bitet beolvas a ESI által mutatott címról, azt kiírja a EDI által mutatott címre, majd továbblépteti 4-gyel a megfelelő irányba (ezt a EFLAGS egy bitje mutatja). Ha emellé odateszünk egy REP/REPZ vagy REPNZ prefixet, akkor ezt annyiszor ismétli, amennyi ECX értéke kezdetben (meg ha időközben a ZERO FLAG beállt/törlődött, összehasonlító jellegűeknél, mint CMPSD, SCASD). Viszont ezek némelyikének is van 8 bit-es argumentuma. Rengeteg utasítás tartozik ide is.
    - De hasonló jellegű utasítás máig az integer osztás (osztandó EDX:EAX-ben, eredmény EAX-be, maradék EDX-be), a port-I/O utasítások, és még sokan mások. Ezek egy 1 opcode byte-osak, egyik operandusuk fix, a másikat mondja egy ModR/M(+SIB) mondja meg.

    Még leírni se egyszerű. És ez még csak a kezdet, a rendszerutasításokat, mint time-stamp counter, controlregister-ek, descriptor table register-ek kezelését hagyjuk inkább.

    Az x87 utasítások opcode-ja 2 byte-os, az első (talán) 9 bit megegyezik, a többi 7 kódolja a műveletet, és hogy memóriacím vagy register a másik operandus, ha előbbi, jön a ModR/M(+SIB).

    Időközben elfogytak az egybyte-os opcode-ok, így a 486-os időkben már egy 0Fh byte-ot is fel kellett venni az új utasítások elé (pl. CPUID, de azért csak sikerült pl. 8 darab BSWAP utasítást elhelyezni a táblában...), mint 'alternative instruction set' prefix, ez később elvesztette prefix jellegét (a decode-nál nem volt mindegy P6-ig), az MMX és az SSE1 utasításokban mindben benne van. Az AMD a három dínó idején egyszerűen megduplázta a 0F prefix-et, így ők már akkor 3 byte-os opcode-nál tartottak.
    Az Intel sem fért bele teljesen a 2 byte-os opcode-ba SSE1 esetén, ezért ők mást találtak ki. A skalár és vektorutasítások (ADDSS és ADDPS) abban különböznek, hogy a skalár előtt ott egy REPZ prefix, innen ugyanaz a formája, mint a vektoros alaknak.
    De az SSE2-t is ábrázolni kellett valahogy, az megint több, mint 140 utasítás, itt az 66h adatméret-váltó prefixet tettek az SSE1 és az MMX megfelelő alakjai elé. De hogy ne legyen egyszerű, a skalár floating-point SSE2 előtt nem 2 prefix van (66h és REPZ), hanem csak egyetlen REPNZ.

    Az egészet fejeljük meg 64 bitben egy REX prefix-szel, hogy a plusz 8-8 általános és XMM registerhez hozzá tudjunk férni. Mindezek mellett az AMD és az Intel is bizonyos esetekben azt ajánlja, hogy NOP műveletek helyett a kód (cikluskezdés) 16/32 byte-os határra illesztését inkább az opcode-dal együtt nem értelmezhető prefix-ekkel tegyük meg (pl. x87-műveletek elé betett REPZ prefix-szel), mert a NOP-pal szemben ezek nem foglalnak macro-op és micro-op helyet...

    A kód a kódban, tehát hogy egy ugró utasítás célja egy másik utasítás azonnali értékének valamely belső byte-ja, ami onnan önmagában is értelmes utasítás, még 8 éve is egyetemi vizsgatétel volt assembly-ből.

    Nagyon egyszerűen: code segment az alapméretet adja, akárhány prefix lehet az opcode előtt, az opcode mondja meg az azonnali érték létét és hosszát, valamint, hogy van-e ModR/M byte, a ModR/M mondja meg, hogy van-e SIB byte, és utóbbi kettő, hogy van-e a címhez is azonnali érték. Mindezt módosítják a prefixek.
    Erre mondják azt sokan, hogy ki kellene dobni az egészet a fenébe, és egy teljesen új (lehetőleg regularized instruction field alapú) utasításkészletre áttérni.

    SSE2+ persze, addigra már megártott a sok .PDF, amit aznap olvastam :)

    [mod]: ...ha csak egyszer sikerülne pársoros hsz-t összehozni.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #657 üzenetére

    Mivel a ModR/M byte mind a 256 féle lehetősége értelmes, a SIB byte 256 féle lehetősége ugyancsak, illetve a prefixek és 1 byte-os opcode-ok egyesítve megintcsak kitöltik majdnem teljesen a 0..255 teret (egy-két üres hely lehet már csak benne, a REX prefix-nek is találtak még üres elemet, ezen kívül a 0Fh prefix és két segment override prefix jött be összesen utólag 8086 óta, mindkettő 386/486 alatt), azonnali értékek és címeltolások bármik lehetnek, így gyakorlatilag tetszőleges belépési pontban értelmes utasítás találtható.

    Az utasítás meghatározásához pontos belépési pontra van szükség, lehet ez ugrás, CALL, RET, INT célja, vagy az előző utasítás vége.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #662 üzenetére

    Egy darabig az AMD tartotta magát az Intel-étől teljesen nomenklatúrához (például micro-op vs uop), teljesen jogosan, mert a funkcionális egységek azonos szinten is teljesen mást jelentenek (Intruction Control Unit vs Reorder Buffer), ez mára egy kissé összefolyt, nem kevés kavarodást okozva.

    Kezdve ott, hogy az AMD-féle micro-op sokkal általánosabb, mint az Intel uop-ja, tekintve, hogy ez utóbbinak legfejlebb 2 bemeneti értéke lehet. Egyszerűbb esetben pl. az olyan utasítások, amik bemeneti értékként valamely flag-et is használják, mint az ADC reg,reg (eredmény=reg+reg+CarryFlag) két uop-ra fordulnak Intel esetében. Bonyolultabb ez eset címzéseknél: általános címzési forma a reg+reg*scale+offset (scale lehet 1,2,4,8), PPro-tól kezdve egészen Core2-ig lehet találni olyan információkat, hogy ezesetben nem egy uop-ra fordul a címgenerálás, hanem bizonyos ALU-műveletek sorozatára, ha nem csak reg, reg+reg vagy reg+offset formájú. Kardinális kérdés ez Netburst alatt lett amiatt, hogy a reg*scale kiszámítását egy külön shift uop végezte, aminek futása egyetlen portra korlátozódott (port1, complex ALU), 3 órajel latency mellett. Prescott-tól talán ezt kiküszöbölték, a latency 1 lett, kérdés, hogy mennyi maradt meg ebből a Core-családra.

    uop-fúzió két esetben lehet Intel-nél, read-modify és store. Előbbinél a load+op egyesül (ez hasonló az AMD load+op macro-opjaihoz), utóbbi azt a kellemetlenséget szünteti meg, hogy egy store operation két uop-ból áll, store_address és store_data (maga a transfer), ezek két külön portra kerülnek még mindig, de decode után együtt haladnak. Ez AMD-nél egy uop, és ha hozzávesszük, hogy egy load+op+store (= op mem,reg) AMD-nél egy macro-op két micro-oppal, Intel esetében összesen 4 uop, amiból a load+op és a store_address+store_data két fuzionált párost alkot, akkor is lemaradásban van még AMD-vel szemben. Ráadásul ez a uop-fúzió a decode utáni lépés, tehát a 4-1-1-1 decoder-szélesség még mindig limitáló tényező.

    Makro-opfúzió csak igen speciális esetekben lehet, csak bizonyos feltételes ugrások (amik nem kezelik az Overflow Flag-et, tehát csak előjeltelen esetben) és az azokat közvetlenül megelőző CMP vagy TST utasítások (amik céloperandusa nem memória) fuzionálhatnak. Ráadásul mindez 64 bites módban nem is működik Core2 esetén.


    Ez előző, AMD pre-decode-dologhoz: zavart eléggé, hogy tényleg erőltetettnek tűnt az én mondatértelmezésem Raymond-ével szemben. Kérdezted, hogy ''lehetséges-e tetszőleges ponttól kezdve keresni a köv. utasítást'', erre én írtam, hogy ''gyakorlatilag tetszőleges belépési pontban értelmes utasítás találtható.''. A kettő nem zárja ki egymást, ha adott egy egység, ami párhuzamosan a beolvasott 16 (K10 esetén 32) byte-os ablak minden egyes byte-jától meghatározza az ott kezdődő utasítást. Kevesebből nem érdemes, az nem visz előre. Ha ez megvan, és adott az első utasítás belépési pontja, akkor akár egy órajel alatt ki lehet szűrni a 16/32 utasításból a valósakat. No de ki az az őrült, aki ilyen nem egyszerű felépítésű egységből - ismernie kell a teljes utasításkészletet - 16-ot (vagy 32-t) tesz egymás mellé egy CPU-ban.
    Hát, az AMD az. Az adott szekcióból Raymonddal szinte az összes mondatot beidéztük, gyakorlatilag csak a lényeget nem (én teljesen átsiklottam felette akkor).
    A second instruction can not be decoded until the length of the first instruction is known. The start position of the second instruction depends on the length of the first instruction. The massively parallel pre-decoder avoids this problem by first pre-decoding the 16 possible instructions in parallel. Each instruction starts at one of the 16 byte locations of the 16 byte line. It then filters out the real instructions with the help of the program-counter which points to the start byte of the next instruction, depending on where we jump into the 16 byte line.
    [link]
    K8 esetében egy-egy alapegység 1 órajel alatt meghatározza az adott byte-nál kezdődő utasítás hosszát és jellegét, 4-4 ilyen egység egy blokkba van rendezve, és 4 ilyen blokk van. Tehát 1 órajel alatt megjön az eredmény mind a 16 egységtől, amiből következő órajelben a Start/End Fixer/Sorter az ismert belépési pont segítségével kiszűri, melyek a valós utasítások. Intel esetében valószínűleg (legalábbis bizonyos esetekben) áll a step-by-step predecode, márcsak abból kiindulva, hogy az adatméret- vagy címméret-módosító prefix-szel ellátott utasításokat újra kell értelmeznie, információk szerint 6 órajel alatt (adatméret prefix pedig ismerős lehet SSE2-utasítások esetén).


    Kicsit leült a hét elején, én az utóbbi 5 napban a Csabai Sör- és Csülökfesztiválra fordítottam az érdeklődésemet. Mondjuk inkább a sör-részére, mert csülköt én is tudok elkészíteni, sört viszont nem tudok főzni :)

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz dezz #686 üzenetére

    Általában a hivatalos gyártói dokumentációkra szeretek hagyatkozni, de én is ezt olvasgatom már majd' két hete. :) A szerző és foglalkozása, a last update dátuma és a bevezető általános leírások meggyőzőek.

    Hivatalos Intel guide-okban én eddig csak a uop formát találtam.


    A Prescott-os mondatom utólag olvasva eléggé félresikeredett, arra gondoltam, hogy abból, hogy a komplex címek ALU-uopok sorozatára fordulnak le, abból mennyi maradt meg (PPro-P2-P3 is 1 órajel alatt számolt shift-et, de már azok optimization guide-jában is szerepel, hogy it may change in the future implementations, Változott, nem előnyére). Végülis Core-vonalon nincs double-pumped ALU, és se a decode, se a uop-fusion nem hatékony, ha egy [esi+edi*8+record.offset] címszámítás 3 vagy 4 uop-ra fordul le.

    És így végiggondolva, az Intel CPU-k hatalmas uop-gyárak, egy egyszerűnek látszó utasítás is elég sok uop-ra fordulhat le. Emellett a 4-1-1-1 decoder-felállás és a ROB mérete igen szűkös.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz VaniliásRönk #804 üzenetére

    Az Intel-nek akkor már kész volt - nem kevés befektetés árán - az IA-64, hozzávaló OS-körítéssel együtt, igaz csapnivaló 32 bites teljesítménnyel. Csak ebbe belebukott, nem úgy, mint a Pentium Pro-ba.

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #805 üzenetére

    Ha szó szerint veszem, amit mondasz, ''SSE1-re lenne optimalizálva'' (úgy értem, nem csak két darab 32 bites értékről van szó), akkor nem tűnik gyorsabbnak. Software Optimization Guide for AMD Athlon™ 64 and AMD Opteron™ Processors dokumentáció szerint PFADD reg,reg lappangása 4 (2x32 bit FP), ADDPS reg,reg lappangása 5 (4x32 bit FP), FPMUL/MULPS hasonlóan (SSE-comment mindkettőre: ''The low half of the result is available one cycle earlier than listed''. (Bár ez nem is meglepetés, tekintve, hogy K8-on egyetlen '3Dnow! and SSE dual 32 bit floating point unit'' van, és az SSE1-utasításokat két 2x32 bites egységben futtatja.)
    ''egy bittel pontosabb is'' Nem hiszem, hogy ezt az IEEE-szabvány megengedné.


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz ftc #809 üzenetére

    Szvsz ez sokkal mélyebben/régebb óta gyökeredzik. Gyakorlatilag ez tartja fenn az x86-család életképességét azóta is, a mai napig.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz Raymond #812 üzenetére

    Az én álláspontom szerint az x86-család fennállását »egyedül« a kompatibilitás tartja fenn (akár visszafelé, akár egymással). Ha kijönne egy olyan generáció, amikor ténylegesen dönteni kellene a gyártók (vagy a korábbi, vagy a mostani párhuzamos generációk) között, akkor mennyien gondolkodnának el más platformon (azonos költségek mellett)? Az x86 egyetlen ütőkártyája, hogy ezt 2x éve nem adta ki a kezéből; egy Core2 futtatja az XT-s programokat.
    És akárhány gyártó benne volt ebben az szegmensben, arra mindre igaz ez. Az Intel verseny nélkül a saját feldarabolásával játszik. És akkor ráadásul a daraboknak kellene egymás versenytársainak lennie.... (Kapitalista piacon mindenből kettőnek kell lennie.)


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #65675776 #816 üzenetére

    Az Intel pedig kijelentette, hogy asztalon egyelőre nincs helye 64 bitnek, mert minek.
    És csinált megint egy olyan váltást, mint a PPro idején, amikor a 16 bites futásteljesítmények visszaestek 10-20 (itt még több) százalékkal. (Ksiklás a Pentium-vonalon? Ilyen címmel jelentek meg akkoriban a cikkek a Pentium Pro-ról. Az Intel azóta is ebből a microarchitecture-ból él)


    VaniliásRönk: Az AMD is ezt mondja az 2. generation X2 óta, cache-mennyiségben :DDD

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #815 üzenetére

    Nem egészen értem, én vagyok az emlegetett 'harmadik fél', vagy ő: [link]

    Bevallom, soha nem próbáltam 3DNow!-t, mert nem találtam megfelelő alkalmazási helyzetet (vagy nagy adattömb volt adott, vagy két érték, de akkor legalább 64 bites részértékek kellettek). De nem »hiszem«, hogy lehetséges lenne más eredményt adni 3DNow!-ban, mint SSE-ben és 32 bites FPU-ban, akármelyik platformon.


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #826 üzenetére

    Tényleg így van. Akkor viszont nem világos a következő:
    Az PFRCP egy belső ROM-táblából veszi a közelítő értéket, latency 3 mellett. (''An (on-chip) ROM-based table lookup can be used to quickly produce a 14-to-15-bit precision approximation of 1/b using just one 3-cycle latency PFRCP instruction''). Az RCPSS lappangása 3, RCPPS-é 4 (a 2 db 2x32 bites adategység miatt), ez alapján ezek is lookup-táblából kell vegyék eredményüket. Ekkor viszont három eset lehetséges:
    - van egy ROM-tábla az PFRCP-nek és egy külön ROM-tábla az RCPxS utasításoknak, ezek eredménye különbözik, viszont utóbbi ugyanazt adja, mint Intel esetében?
    - az, hogy egy közös ROM-tábla van, és PFRCP és RCPxS utasítások eredménye megegyezik, viszont utóbbi nem ugyanazt adja, mint az Intel-nél, nem jöhet számításba, mivel RCPxS hibahatárának az AMD is az említett relative error-t adja meg,hacsak nem
    - a két hibamegjelölés ugyanazt a pontosságot takarja?
    Tudnál mondani konkrét példaértéket, ahol kijön a különbség? (Itanium esetében az Intel a RCPxS pontosságát már 2^-17 nagyságrendben jelöli meg, de ez nem hiszem, hogy változhatna x86 esetében a jövőben.)


    Ha jól értelmezem, akkor K8 esetében precíz, 24 bites a/b értékek kiszámítása PFRCP-PUNPCKLDQ-PFRCPIT1-PFRCPIT2-PFMUL (latency 4-2-4-4-4) sorozattal jóval gyorsabb, mint DIVPS (latency 33) használatával? És ha jól látom, az előbbi minden utasítása fully pipelined, tehát több sorozat is futhat egymás mellett, sorozatonként 2-2 register használatával? (És akkor ez még K10 esetében is áll, hiába fele a DIVPS lappangása, ha nem pipe-olt). Ezt megjegyzem :)

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #877 üzenetére

    Köszönöm, nem kevés időt szántál rá.
    Nem kapaszkodtam elég erősen.
    Először azt hittem, n=1 elírás, de itthon K7-en kipróbáltam, valóban. Sőt, a 3DNow! a kettő hatványaival konzekvensen gondban van, SSE csak 1 esetében. (A példák K7-es eredményei megegyeznek a K8-eredményeiddel. Gondolom, az eltérő első SSE-megvalósítások után fontosabb volt a backward compatibility.)

    Igen, meg lehet valósítani, néztem utána a képletet, legfeljebb annyit lehet felhozni ellene, hogy a nagyobb SSE-kód (a 2 menet 2 MOVAPS, 2 MULPS, 1-1 ADDPS és SUBPS, teljes függőségben, ahogy én írtam) csökkenti az ütemezők előrelátási tartományát, illetve az összes 3DNow! DirectPath, a vector SSE-utasítások DirectPath Double-k K8-on. Amúgy nem lehetséges, hogy ezek miatt gyorsabb pár százalékkal a 3DNow!-kód?

    alienpapa, VaniliásRönk: én dezz-ék DX10-es beszélgetésére néztem hasonlóan. De gondolom, akosf hsz-e után tisztább a kép.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz ftc #882 üzenetére

    A válaszlehetőség természetesen az övé, de a fent említett 3 dolog egy-egy utasítás kimenete, assembly szinten.
    Az AMD esetében x értékre két 1/x gyors közelítő reciprokképzési elemi utasítás adott, a 3DNow!-féle PFRCP és az SSE1-féle RCPSS (vagy RCPPS, előbbi egy értékre számolja, utóbbi négyre, itt mindegy). Az Intel CPU-k csak az SSE1-es utasításokat ismerik, ugyanazokat. A dolog ott vált érdekessé, hogy 3DNow! esetében az AMD bevallottan tényleges számítás helyett egy ROM-táblázatból veszi az eredményeket, 'futásidő' alapján az SSE1 esetében is. Az Intel nem nyilatkozik erről. És mindhárom eredmény különbözhet.


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #688 üzenetére

    Tekinthető alapnak ez Intel-dokumentáció (http://www.intel.com/design/processor/manuals/248966.pdf)
    Adós vagyok még egy válasszal ROB-ra vonatkozó kijelentésemre, de először egy helyesbítés: a 4-1-1-1 bottleneck Core2-re nem igaz. Valóban 4-1-1-1 felállás, de nem μop-ok a decoder-ek kimenetei, hanem micro-fused μops, ezt a fenti .pdf konzekvensen használja minden említéskor (nem úgy, mint egyes dokumentációk, amik oldalakon keresztül vezetik le, hogy „single-μop”, aztán a végére odabiggyesztik egy mondatba, hogy ezek valójában micro-fused μops…)
    ''All decoders support the common cases of single μop flows, including: micro-fusion, stack pointer tracking and macro-fusion. Thus, the three simple decoders are not limited to decoding single-μop instructions. Packing instructions into a 4-1-1-1 template is not necessary and not recommended. ''
    Ezt bottleneck-nek tekinteni semmiképpen nem lehet, még az AMD megoldásánál is szélesebb, ezt így ebben a formában csak sorozatos op [mem],reg vagy microcode-ROM alapú utasítások tudják megfogni.


    Intel ROB (és egy kicsit tágabban, és ott nem csak Core2-re gondoltam):


    - Az Intel CPU-k esetében semmi nem cáfolja azt, hogy egyetlen, 128 bites egységméretű register-file van, tehát az integer register-ek is 128 bitesek (Core2 esetében kibővítették az integer ALU-kat is 128 bitre, ezek végzik pl. a SSEx logikai műveleteket, az int-FPU és FPU-int átkapcsolás ára egy + órajel késleltetés). Ha egy register-file van, akkor az rename miatt annyi belső register-nek kellene lennie legalább, ahány elemű a ROB (minden micro-fused μops-nak jusson más-más célregister átnevezés után) + a véglegesített register-értékek + a belső átmeneti register-ek. Ezt a nagy register-filet írni/olvasni kell, ez a ROB feladata. Emellett a μop-okban található argument placeholder-ek (hogy mondják ezt magyarul két szóban?) is 128 bitesek, az AMD 64 biteseivel szemben.


    - Intel esetében egy hosszabb kódot figyelembe véve valamivel több μops keletkezik fordítás után, mint amennyi micro-op AMD esetében, ez a szám nem csökkent számottevően az idők folyamán (ennek egyik oka, hogy az Intel alapból 128 bites register-file-t és default adatméretet alkalmaz, Netburst óta biztosan, az execution pipe-okban törtek ketté 64 bites elemekre a 128 bites adatok, majd fűződtek össze, növelve a késleltetést). K10 esetében viszont drasztikusan csökken a szükséges micro-opok száma 128 bites műveletek esetében, felére. De nézzük végig a ROB- és Reservation Station-méreteket, utóbbi adja nagyjából az adott CPU out-of-order előrelátási mélységét:
    Core2:
    ..ROB: 96 micro-fused μops
    ..RS: 32 entries
    ..decoders: 4-1-1-1 micro-fused μops/cycle
    ..ROB read ports: 2 (3?)
    ..Possible input registers/cycle: 4x3
    Pentium M/Core Solo-Duo:
    ..ROB: 40 micro-fused μops
    ..RS: 20 entries
    ..decoder: 4-1-1 micro-fused μops /cycle (128 bites műveleteknél csak store-fusion van, load-op fusion nincs)
    ..ROB read ports: 2
    ..Possible input registers/cycle: 3x3
    Netburst:
    ..ROB 126 μops
    ..RS: N/A
    ..decoder: 4 μops/cycle + trace cache
    ..ROB read ports: N/A
    PPro/P2/P3: ROB:
    ..40 μops
    ..RS: 20 entries
    ..decoders: 4-1-1 μops/cycle
    ..ROB read ports: 2
    ..Possible input registers/cycle: 3+2+2
    K7:
    ..Instruction Control Unit: 72 macro-ops (up to 144 micro-ops)
    ..Integer Scheduler (18 macro-ops) + FPU scheduler (36 macro-ops): 54 macro-ops (up to 18*2+36=72 micro-ops)
    ..IFFRF-read + FPU register-file read: 9+5/cycle
    ..Possible input registers/cycle: 3x3 + 2+2+1
    K8, K10:
    ..Instruction Control Unit: 72 macro-ops (up to 144 micro-ops)
    ..Integer Scheduler (3x8 macro-ops) + FPU scheduler (36 macro-ops): 60 macro-ops (up to 24*2+36=84 micro-ops)
    ..IFFRF-read + FPU register-file read: 9+5/cycle
    ..Possible input registers/cycle: 3x3 + 2+2+1


    - Hogy miért emeltem ki a ROB – Register File olvasások számát? Van itt egy súlyos maradvány bottleneck Core2 esetében:
    As a μop is renamed, it determines whether its source operands have executed and been written to the reorder buffer (ROB), or whether they will be captured “in flight” in the RS or in the bypass network. Typically, the great majority of source operands are found to be “in flight” during renaming. Those that have been written back to the ROB are read through a set of read ports.
    Since the Intel Core Microarchitecture is optimized for the common case where the operands are “in flight”, it does not provide a full set of read ports to enable all renamed μops to read all sources from the ROB in the same cycle.

    When not all sources can be read, a μop can stall in the rename stage until it can getaccess to enough ROB read ports to complete renaming the μop. This stall is usually short-lived. Typically, a μop will complete renaming in the next cycle, but it appears to the application as a loss of rename bandwidth. Some of the software-visible situations that can cause ROB read port stalls include:
    • Registers that have become cold and require a ROB read port because execution units are doing other independent calculations.
    • Constants inside registers
    • Pointer and index registers
    In rare cases, ROB read port stalls may lead to more significant performance degradations. There are a couple of heuristics that can help prevent over-subscribing the ROB read ports:
    • Keep common register usage clustered together. Multiple references to the same written-back register can be “folded” inside the out of order execution core.
    • Keep dependency chains intact. This practice ensures that the registers will not have been written back when the new micro-ops are written to the RS.
    These two scheduling heuristics may conflict with other more common scheduling heuristics. To reduce demand on the ROB read port, use these two heuristics only if both the following situations are met:
    • short latency operations
    • indications of actual ROB read port stalls can be confirmed by measurements of the performance event (the relevant event is RAT_STALLS.ROB_READ_PORT, see Appendix A of the Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3B)
    If the code has a long dependency chain, these two heuristics should not be used because they can cause the RS to fill, causing damage that outweighs the positive effects of reducing demands on the ROB read port.

    Annyira zavaró ez a szűk keresztmetszet, hogy az Optimization Reference szerzője tanácstalan, hogy hogyan kellene elkerülni. És ezalapján egy sima
    movaps xmm1,[esi+ecx]
    addps xmm1,[edi+ecx] vagy xorps xmm1,xmm2
    movaps [ebx+ecx],xmm1
    sub ecx,edx
    jg …
    ciklus is meggátolja a 4 micro-fused μop/cycle ROB-ba érkezését. (amennyiben esi, edi, ebx mondjuk tömbcím, tehát konstans, és az xmm2 mondjuk egy előjelváltó 128 bites konstans, edx-ben pedig a 10h érték van, előre felvett konstansként).

    - Az Intel esetében a Reservation Station osztja el a μop-okat a 6 execution unit között. Minden órajelben hatot kell találnia a 6 execution port-ra („vízszintes” irány) out-of-order („függőleges” irány). Ezzel szemben AMD esetén a scheduler-eknek 9-et (dezz #734, raymond #736: jó az a kilences szám, 9 execution unit van, legalábbis K7 óta. Nem újdonság, de úgy látszik, nem minden csoda tart csak három napig. :) Az, hogy az „instructions” hogy került mögé „micro-op” helyett, azt a szerző tudja…), viszont fixed-issue miatt csak függőlegesen kell keresnie. Ez a fixed-issue végigvezet majdnem a teljes CPU-n, kissé VLIW-felépítésre emlékeztetve, meglehetősen leegyszerűsítve sok mindent (decode, execution, retirement…)

    Én nem nagyon látom, hogy az Intel-ek egyszerűbb felépítésűek lennének, mint az AMD-k (1 horizontal-vertical vs ’6’ vertical ütemező, egy, 128 bites közös register-file vs két, 64 bites register-file - integer oldalon valós rename nélkül -, RISC vs valamennyire VLIW felépítés). Sőt. A bonyolultabb/nagyobb adatméretű ROB- és RS felépítést az Intel általában az execution unit-ok egyszerűsítésével szokta ellensúlyozni (mint pl. amit említettem, hogy közös ALU van az integer (8-16-32-64 bit), az MMX (64 bit) és az SSEx (128 bit) logikai műveleteknek; vagy annak idején a közös integer-FMUL egység, stb.).
    Azt is azért látni kell, hogy a K7-K8-K10 vonal kevesebbet változott az idők folyamán szerkezetileg, mint mondjuk csak a Netburst a Villamette és a Presler között. Erre lehet azt is mondani, hogy mert olyan hatékony, azt is, hogy az AMD-nek nincs annyi feljesztési tőkéje, mint az Intel-nek, de ez már inkább hitvita és szimpátia kérdése, mint szakmai. Az, hogy az AMD miért nem tudja járatni ezt az alapvető felépítést az annak „megfelelő” órajelen (szerintem egy olyan 4 GHz-ig kellene dual-core K10-nek skálázódni, hogy megoldódjanak rövidtávon az AMD problémái), ezt pontosan nem tudjuk, de lehetséges, hogy ennek okát nem a szűk értelemben vett magokon belül kell keresni.


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz ftc #901 üzenetére

    Erre én tényleg nem tudok válaszolni, a többiek szerintem (akosf biztosan) sokkal több érdemlegeset tudnak mondani erre a kérdésre. Én mindent assembly-ben írok már pár éve(, legyen az hot-spot, vagy csak egy egyszerű háztartási kód, pl. egy szövegbeviteli mező), Delphi fordítja. Szeretem szó szerint viszontlátni ugyanazt a CPU-window-ban, mint amit beírtam. :)

    Privát vélemény: a fordítókat szükséges rossznak tartom, általában alapszintű kódot csinálnak a saját beírt magas szintű programodból, messze nem olyan hatékonyat, hogy gyártók közti különbségekről lehessen beszélni. Nem kis részben ennek köszönhető a CPU-k fejlődésének egy-egy irányvonala is (jó példa a stack-engine mindkét részről, de mondjuk magáért az out-of-order-ért sem kevésbé felelősek a compiler-ek).
    Más kérdés a beépített függvénykönyvtárak, főleg a math library-k. Azok egy része azért kézzel készül, ott lehetnek különbségek. De általában csak ilyen szinű trükközések vannak: [link]


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #932 üzenetére

    Meg akartam kérdezni, milyen jellegű kódnál jött ki a 87% különbség, de nem kérdezem, nincs miért. Dezz, megnéztem, aztán a K8 idevonatkozó adatait is.
    K8 retirement-szélesség órajelenként 3 macro-op. De nem DirectPath Double utasítások esetén, ez minden 128 bites SSEx utasításra hatással van. A két 64 bites macro-opnak azonos órajelben, egyszerre kell visszavonulnia, így a 3. slot-ba sem kerülhet másik 128 bites utasítás egy macro-opja. Tehát a retirement leszűkül órajelenkénti 1 vectorSSE utasításra, legyen az op reg,reg vagy op reg,mem. (Ellenben Core2 4 vagy a korábbi Intel-ek 3 értékével, op reg,reg-re nézve...)

    ''Most 128 bit SSE and SSE2 instructions are implemented as double dispatch instructions. Only those that can not be split into two independent 64 bit operations are handled as Vector Path (Micro Code) instructions. Those SSE2 instructions that operate on only one half of a 128 bit register are implemented as a single (Direct Path) instruction.
    [...]
    A disadvantage may be that the decode rate of 128 bit SSE2 instructions is limited to 1.5 per cycle. In general however this not a performance limiter because the maximum throughput is limited by the FP units and the retirement hardware to a single 128 bit SSE instruction per cycle.
    [...]
    Instructions generated by Doubles can mix with other (Direct Path) instructions during decoding and retirement. The two instructions generated by a Double must however retire simultaneously, imagine a PUSH that does retire the memory store but doesn't retire the Stack Pointer update.. This leads to the limitation that both instructions generated by a Double must be in the same 3 instruction line during retirement.''

    Vigasz, hogy K10 esetén DirectPath lesz a 128 bites utasítások többsége, így a decoder-bandwidth kétszeresére, a retirement háromszorosára fog nőni ezek esetében, kisebb latency és nagyobb throughput mellett, ez talán jelentősen megnöveli a SSEx-teljesítményt. Nem alaptalan Dezz [link] elmélkedése.

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz VaniliásRönk #1067 üzenetére

    Meg még sok mindentől, és nem érdemes a Prescott-ra (#1063) hivatkozni e tekintetben, jól tudjuk, hogy akkor az Intel a Netburst-ös 10 Ghz-es cél kitűzésekor egy pillanatig nem gondolta, hogy a fogyasztás egyszer az egyik legfontosabb kérdés lesz, ma pedig mindegyik gyártó igyekszik tartani magát a kimondatlan 130W maximumhoz.
    Azonos tranzisztorszám mellett attól is függ a fogyasztás, hogy mire mennyi tranzisztort szánnak, hogyan rendezik át őket a funkcionális csoportok között, az internal buffer-eket milyen hatékonyan használják ki (pl. trace cache vs μop-fusion), az egyes funkcionális egységek (execution unit-ok) mennyi »felesleges« munkát végeznek (pl. Netburst esetében a μop-sequence replay), egy-egy gyakran lefutó műveletsor mennyi belső erőforrást használ (pl. az újabb és újabb utasításkészletek miatt ezek egy csoportja mindig kevesebb elemi műveletet igényel, mint korábban, tehát kevesebb koordinációt és kevesebb, bár bonyolultabb végrehajtási erőforrást), a teljes pipeline hány lépcsős, etc...
    Mert úgy látom, hogy Penryn vs Conroe felállásból indult a vita, nem csak arról szól, hogy mi lenne, ha a Conroe-t egy az egyben 45 nm-re implementálná az Intel. Persze az adott technológiai kereteken belül mindegyik gyártó igyekszik kihasználni az elfogadható fogyasztási maximumot legfelső szinten, ha belefér, ha mással nem, akkor órajel-emeléssel vagy cacheméret-növeléssel (és sosem csak pár kilobyte-tal nőnek ezek a tárak).


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz VaniliásRönk #1072 üzenetére

    ''A Prescottot azért említettem, mert azt a magas fogyasztása céltáblává tette, ...''
    Szerény véleményem szerint az Intel-nek az volt a balszerencséje, hogy az AMD hagyományosan jó mikroarchitektúrája megfelelően jól teljesített mellette, de ha azzal a felépítéssel akarta volna az Intel legyőzni, a datasheet-eken egy sorban túl nagy különbség lett volna ahhoz, hogy az egyszeri vásárlónak ne tűnjön fel a különbség. Az AMD pont annyira kevéssé tehet arról, hogy jó helyen és időben volt egy jó felépítéssel és teljesítménnyel, mint az Intel arról, hogy nem úgy alakult a jövőbeli vásárlói ''fogyasztásérzékenység'', ahogy azt elképzelte.

    ''Nehezen tudom elképzelni hogy annyira rossz lenne a Conroe szervezése, olyan látványosan pazarolna, hogy ilyen áttervezésnek jelentős tere van, nagy fogyasztáscsökkenés lenne elérhető.''
    Erre majd térjünk vissza később, pl. ha megjelent a Penryn, akkori szemmel a Tualatin is egy kifogástalan felépítés volt, mégis mennyit sikerült továbbfejlődni belőle kiindulva a PentiumM/Core/Core2 vonalon. :)


    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #1106 üzenetére

    Én mondjuk szívesebben vetném össze közel azonos órajelű Core Duo-val, az is 64 bites execution unitokkal rendelkezik, de közelebb áll a Core2-höz felépítésben (micro-op fusion, rövid pipe, shared L2, stb.). Szerintem úgy másképp alakulna az órajellel arányos gyorsulás.

    dezz: immediate x87 és SIMD esetében nem lehet, csak csak x86 utasításokban, nem jelentős, ha elfér 8 biten, akkor úgy ábrázolódik. A címeltolások száma sokkal fontosabb tényező, ez sokkal ritkábban fér el 8 biten (optimalizációs fogás úgy beállítani a pointer-eket, hogy a lehető legtöbb elférjen), ekkor pedig minimum 6 byte-os utasításokkal számolhatunk (SIMD esetben min. 7 byte).

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

  • P.H.

    senior tag

    válasz #95904256 #1110 üzenetére

    Desktop nincs, csak server: Xeon LV. [link]

    [Szerkesztve]

    Arguing on the Internet is like running in the Special Olympics. Even if you win, you are still ... ˙˙˙ Real Eyes Realize Real Lies ˙˙˙

Hozzászólok Aktív témák