5.3.2 · D3 · HinglishAdvanced Microarchitecture

Worked examplesOut-of-order execution

4,972 words23 min read↑ Read in English

5.3.2 · D3 · Hardware › Advanced Microarchitecture › Out-of-order execution

Ye page Out-of-order execution ka drill floor hai. Parent note ne ideas build kiye the: renaming, reservation stations, reorder buffer (ROB), ready condition. Yahan hum har tarah ki situation ko un ideas pe throw karte hain aur har ek ko haath se grind karte hain.

Shuru karne se pehle, ek promise: jo bhi symbol tum yahan dekhoge wo parent note mein earn kiya gaya hai, aur main pehli baar aane par use re-anchor karta hoon. Agar koi term naya lagta hai, wo ek bug hai — mujhe batao.

Is poori page par hum do timing rules follow karte hain taaki har jawab reproducible ho. Inhe ek baar dhyan se padho — ye baad mein har "3 cycles hain ya 4?" sawaal ko resolve karte hain.


The scenario matrix

Jo bhi OoOE tumhare saath kar sakta hai wo in cells mein se kisi ek mein aata hai. Neeche ke examples un cell(s) ke saath labeled hain jinhe wo cover karte hain, aur saath mein wo har row ko hit karte hain.

# Case class Kya mushkil banata hai Covered by
A No dependencies (fully parallel) sirf execution width se limited Ex 1
B RAW chain (true dependency) critical path — ise beat nahi kar sakte Ex 2
C WAW / WAR false dependency parallelism unlock karne ke liye rename karna chahiye Ex 3
D Mixed: slow LOAD + independents + dependent latency ke around reorder karo Ex 4
E Not enough ports (execution-width limit) ready ≠ can-fire Ex 5
F Exception / speculative squash ROB ke through precise state Ex 6
G Degenerate: window too small independent work nahi dekh sakte Ex 7
H Limiting case: zero dependencies, infinite width IPC → execution/retire width Ex 8
I Real-world word problem (cache miss) memory-level parallelism Ex 9
J Exam twist: is a reorder legal? true vs false hazard spot karo Ex 10

Parent note ke teen ceilings har cell ko govern karte hain (IPC = Instructions Per Cycle, upar define kiya): Har example asal mein ye puch raha hai: in teen ceilings mein se kaunsa yahan binding hai? (Example 8 ek hidden chautha ceiling unmask karega: retire bandwidth.)

Neeche ki figure un teen ceilings ko teen bars ki illustrative heights ke roop mein draw karti hai. Green dashed line sabse chhote bar par baithe hai — wo wahi hai jo actually IPC ko limit karta hai. Har example ek hunt hai ki kaunsa bar sabse chhota hai: blue "window" bar (kya tum kaam dekh sakte ho?), yellow "execution width" bar (kya ise run karne ki jagah hai?), ya red "critical path" bar (kya tum sequence karne ke liye majboor ho?).

Figure — Out-of-order execution

Example 1 — Cell A: fully independent instructions

Forecast: padhne se pehle guess karo — kya ye 1, 2, ya 4 cycles hai?

  1. Dependencies check karo. Koi bhi instruction woh register nahi padhta jo dusra likhta hai. To koi RAW, WAR, WAW nahi. Ye step kyun? Dependencies ek hi reason hain jab OoOE serialize karne par majboor hota hai. Zero dependencies matlab critical-path ceiling = 1 (koi bhi single instruction poori chain hai).
  2. Resources check karo. 4 ready instructions, lekin sirf 2 add ports. To cycle 1 mein I1, I2 fire hote hain (ready end cyc 1); cycle 2 mein I3, I4 (ready end cyc 2). Ye step kyun? Ready hona zaroori hai lekin kaafi nahi — tumhe ek free port bhi chahiye. Yahan execution-width ceiling = 2/cycle binding wala hai.
  3. Total-cycles rule apply karo. Execution-bound = last-ready (cyc 2) + 1 = 3. Retire-bound = cycles of committing. ke saath port width se matched, commit pace rakhta hai aur dominate nahi karta; last pair ka total commit cycle 3 mein land karta hai. Ye step kyun? Humne deliberately set kiya taaki retire issue se match ho — ye "commit hides" wala case hai.
  4. Total = 3 cycles (compute karne ke liye 2, + final pair commit karne ke liye 1).

Verify: last result ready cyc 2, +1 commit ⇒ 3. Sustained IPC ; jab ports saturate hote hain to steady-state throughput 2/cycle = execution width hai, binding ceiling. ✓


Example 2 — Cell B: ek pure RAW chain

Forecast: unlimited execution units ke saath, kya ye 1 cycle mein run karta hai?

  1. Data-flow graph draw karo: I1 → I2 → I3 → I4, ek single strand. Ye step kyun? Ek RAW edge ek true dependency hai — I2 ko literally wo number chahiye jo I1 produce karta hai. Koi renaming, koi extra port ise nahi hatata.
  2. Har fire time karo. I1 cyc 1 mein fire, ready end of cyc 1. I2 cyc 2 mein fire karta hai (apne operand ke baad). I3 cyc 3. I4 cyc 4. Ye step kyun? Ye critical-path ceiling kaam kar raha hai. Ye wo ek limit hai jo koi bhi hardware beat nahi kar sakta — Apple ka 600-entry window bhi I4 ko I3 ke khatam hone se pehle run nahi kar sakta.
  3. Total-cycles rule apply karo. Results ek-ek cycle mein nikalte hain (I1 ready cyc1, …, I4 ready cyc4). Execution-bound = last-ready (cyc 4) + 1 = 5. Retire-bound = commits, lekin wo I1 ke khatam hone par start hote hain aur 1/cycle pace rakhte hain — kabhi execution term se bada nahi. To execution term pick karta hai. Ye step kyun? Chains wahi case hain jahan commit hide hota hai: ek cycle mein ek result exactly se match karta hai.
  4. Total = 5 cycles (4 ki chain + ek final commit). IPC ; chain ke saath steady-state throughput 1/cycle hai, critical-path-limited ILP se match karta hai.

Verify: chain length 4 ⇒ last ready cyc 4, +1 commit ⇒ 5. Ports add karna kuch nahi karta: chain ko phir bhi force karta hai. ✓


Example 3 — Cell C: renaming se false dependency khatam

Forecast: kya I3 ko I1 ke khatam hone ka intezaar karna padta hai?

  1. R1 ke har shared use ko classify karo. I2 I1 ka R1 padhta hai (true RAW). I3 phir R1 likhta hai — naam I1 ki write se (WAW) aur I2 ki read se (WAR) collide karta hai. Wo dono false hain: ye sirf isliye exist karte hain kyunki assembler ne naam R1 reuse kiya. Ye step kyun? False dependencies naming ke artifacts hain, data ke nahi. Parent note ki insight: unhe rename karke hatao.
  2. Physical registers par rename karo. Yaad raho P-prefixed names CPU ke bahut saare real storage slots hain. I1 ke result ko physical slot P1 do aur I3 ke result ko ek alag slot P2. Ab:
    • I2 P1 padhta hai, I4 P2 padhta hai. Dono chains {I1→I2} aur {I3→I4} alag physical slots touch karti hain, to wo independent hain. Ye step kyun? Do distinct physical slots use karna do R1 writes ke liye real data-flow graph expose karta hai — ek tangle ki jagah do alag 2-long strands.
  3. Schedule karo (2 ports). Cyc 1: I1 aur I3 fire karo (dono ready, independent), ready end cyc 1. Cyc 2: I2 aur I4 fire karo, ready end cyc 2. Execution-bound = 2 + 1 = 3; retire-bound ke saath pace rakhta hai. Total = 3 cycles.
  4. Bina renaming ke: I3 tab tak stall karta hai jab tak I1 ki write retire ho aur I2 ki read complete ho → serial-ish: cyc1 I1, cyc2 I2, cyc3 I3, cyc4 I4, last ready cyc 4, +1 commit ⇒ 5 cycles.

Verify: speedup . Renaming ke saath, steady-state throughput = 2/cycle = execution width, binding ceiling jab false deps khatam ho jaate hain. ✓

Figure ye split visually dikhati hai: top row wo tangled single strand hai jo naam reuse se force hoti hai (blue → red edges), aur bottom row wo do clean, parallel strands hain jab P1/P2 unhe separate karta hai.

Figure — Out-of-order execution

Example 4 — Cell D: slow LOAD + independents + dependent (parent ka showcase, re-derived)

Forecast: LOAD 3 cycles leta hai — kya I2 aur I4 ko uske peeche baithna padega?

  1. In-order. I1 cyc 1–3 occupy karta hai. I2 tab tak start nahi ho sakta jab tak I1 leave na kare → cyc 4. I3 → cyc 5. I4 → cyc 6. Total 6. Ye step kyun? In-order issue matlab ek stall sabko uske peeche poison karta hai, innocent I2/I4 ko bhi.
  2. Out-of-order — independents ko jaldi fire karo. I2 ka koi operand wait nahi kar raha → cyc 1 mein ALU port par fire karo (ready end cyc 1). I4 → cyc 2 mein ALU port par fire karo (ready end cyc 2). LOAD I1 apne port par cyc 1–3 run karta hai. Ye step kyun? Alag ports = I2/I4 LOAD ke saath compete nahi karte. Ye poora OoOE ka point hai.
  3. Dependent I3 ko fire karo. I3 ko I1 (ready end cyc 3) aur I2 (ready end cyc 1) chahiye. Last operand ready end of cyc 3 → I3 cyc 4 mein fire karta hai, result ready end cyc 4. Ye step kyun? I3 genuine dependency chain I1 → I3 par baitha hai; wo chain, ports nahi, uski earliest fire set karti hai.
  4. Total-cycles rule apply karo. I3 program order mein last hai; uska result ready end cyc 4. Execution-bound = 4 + 1 = 5. Retire-bound: sirf 4 instructions, par order mein committed lekin koi burst nahi (I2 ready cyc1, I4 cyc2, I1 cyc3, I3 cyc4 — ek per cycle) to retire pace rakhta hai aur dominate nahi karta. 5 deta hai. Ye step kyun? Parent note ka "≈5 cycles including commit settling" exactly yahi hai: +1 wo last result I3 ka in-order commit hai.

Verify: speedup , parent note se match. Residual latency = genuine chain I1→I3 plus ek in-order commit step. ✓

Timeline figure dono schedules stack karta hai: top block in-order hai (I2/I4 red LOAD ke peeche stranded, red dashed finish line at 6), bottom block out-of-order hai (I2/I4 jaldi pull hue, green dashed finish line at 5).

Figure — Out-of-order execution

Example 5 — Cell E: ready ≠ can-fire, AUR retire bandwidth bite karta hai

Forecast: sab chhah turant ready hain — to kya 1 cycle? Ya kuch aur cap karta hai?

  1. Sab ek saath ready. Koi dependency delay nahi. Critical-path ceiling = 1. Window kaafi bada hai (window ceiling ≥ 6). Ye step kyun? Do ceilings kehte hain "jao fast"; humein binding wala dhundhna hai.
  2. Ports starting ko limit karte hain. 2 per cycle: cyc 1 mein I1,I2 fire (ready end 1); cyc 2 mein I3,I4 (ready end 2); cyc 3 mein I5,I6 (ready end 3). Ye step kyun? Execution-width ceiling = 2/cycle. Execution akela end of cyc 3 tak results produce karna khatam kar deta.
  3. Ab total-cycles rule honestly apply karo. Chhah results ek burst mein ready hote hain (2 per cycle) lekin retire sirf per cycle drain karta hai. Retire-bound = commit cycles. Committing cyc 2 mein start hoti hai (I1/I2 ready hone ke ek cycle baad) aur 1/cycle proceed karta hai: I1 cyc2, I2 cyc3, I3 cyc4, I4 cyc5, I5 cyc6, I6 cyc7. Ye step kyun? Sirf ek retire slot ke saath, chhah results sabhi cycle 4 tak committed nahi ho sakte. Retire term jeet jaata hai. Execution-bound = 3+1 = 4, retire-bound cyc 7 tak pahunche → Total = 7 cycles.
  4. Yahan binding ceiling retire bandwidth hai, ports nahi. Ports ROB ko finished results se bhar dete hain usse zyada fast jitna commit publish kar sakta hai.

Verify: results end cyc 3 tak produce hue, lekin in-order commits cyc 2 se start ⇒ last commit cyc . Sustained IPC (retire bandwidth), asli bottleneck. ✓ Lesson: readiness tumhe choose karne deti hai, ports decide karte hain kitne start hote hain, retire decide karta hai kitne real bante hain.


Example 6 — Cell F: exception speculative kaam squash karta hai

Forecast: kya I4 aur I5 ke results survive karte hain, kyunki wo finish ho gaye?

  1. I3 head par hai aur fault hua. Commit rule: Can_Commit(I_h) = Complete(I_h) ∧ ¬Exception(I_h). Yahan Exception true hai → commit nahi ho sakta. Ye step kyun? Sirf head commit kar sakta hai, aur wo exception-free hona chahiye. Ye sequential execution ka illusion enforce karta hai.
  2. Head ke baad sab kuch flush karo. I4, I5 faulting I3 se younger hain. Unki computed values sirf ROB mein rehti hain, kabhi architectural registers/memory mein nahi → unhe discard karo. Ye step kyun? Precise exceptions demand karte hain: "I3 fault hua, uske baad kuch nahi hua." Speculative results invisible hone chahiye.
  3. Roll back. Architectural state = I2 ke baad wali state (I3 ke inputs). OS ko signal karo.

Verify: committed set = {I1, I2}; discarded = {I4, I5}; I3 pending. Visible results ki count = 2, exactly pre-fault instructions. ✓ (Same principle speculative execution aur branch prediction recovery ko power karta hai.)


Example 7 — Cell G: window itna chhota ki kaam dikhi hi nahi

Forecast: I50 par free independent work hai — kya OoOE use pakdega?

  1. Window sirf 4 in-flight instructions hold karta hai. Jab I1 miss par stall karta hai, window I1..I4 se bhar jaata hai aur aage nahi badh sakta (I1 ROB head ko commit karne se rokta hai). Ye step kyun? Window ceiling cap karta hai ki CPU kitna aage dekh sakta hai. I50 window edge se 46 instructions aage hai — invisible.
  2. CPU ~100 cycles stalls karta hai kuch useful kiye bina, chahe I50 run ho sakta tha. Ye step kyun? Independent kaam jo tum dekh nahi sakte wo independent kaam hai jo tum use nahi kar sakte. Exactly isliye modern chips windows 200–600 entries tak badhate hain.
  3. Fix: window ≥ 50 I50 ko andar pull karta aur miss latency ko uske peeche hide karta.

Verify: window 4 ke saath, miss ke dauran reachable-independent-work = 0 → binding ceiling window-limited ILP ≈ 0 useful IPC un cycles ke liye. Window 50 se bada karo → I50 reachable. ✓


Example 8 — Cell H: clean limit (infinite width) — retire bandwidth unmask karna

Forecast: kya IPC forever badhta hai, ya kisi wall se takrata hai?

  1. Execution jaldi khatam hota hai. Zero dependencies ke saath critical-path ceiling = 1 (kabhi bind nahi karta) aur window ≥ N (kabhi bind nahi karta), to execution sirf cycles chahiye. ke saath, wo 1 cycle hai: har result cycle 1 ke end mein ready hai. Ye step kyun? Humne teen ceilings deliberately hataye jo bhi bacha hai use expose karne ke liye.
  2. Lekin results abhi bhi committed hone chahiye order mein /cycle par. Saare results publish karne mein commit cycles lagte hain. Total-cycles rule apply karo: . Ye step kyun? Infinite ports useless hain agar tum sirf /cycle par results real bana sako. Ye hidden fourth ceiling hai — retire bandwidth.
  3. Limit lo. ke saath, sustained IPC ( ke liye). To IPC retire width par saturate hota hai, kabhi nahi infinite execution width par. Ye step kyun? Ye pin down karta hai ki kaunsa ceiling truly independent kaam ke burst ko cap karta hai: ports nahi, balki commit.

Verify: . ke saath: total , IPC . ke saath: total , IPC (aur , kyunki ). ke saath: IPC , nahi se capped. ✓ IPC , confirm karta hai retire bandwidth wall hai.


Example 9 — Cell I: real-world word problem (memory-level parallelism)

Forecast: do 100-cycle misses — kya wo 200 cycles hai ya 100?

  1. In-order (blocking). I1 pipe ko 100 cycles stall karta hai, phir I2 aur 100 stall karta hai → ~200 cycles, phir I3. Ye step kyun? Ek blocking machine misses serialize karta hai chahe wo independent hoon.
  2. Out-of-order + non-blocking loads. I1 aur I2 independent hain → dono miss requests same window mein launch hote hain. Memory unhe overlapped serve karta hai (memory-level parallelism). Dono ~cycle 100 par wapas. I3 cycle 101 mein fire karta hai. Ye step kyun? OoOE ka window dono miss addresses ek saath outstanding rehne deta hai — misses ~100-cycle wait share karte hain stack karne ki jagah. 100-cycle miss ke against sirf 3 instructions ka commit at 1/cycle trivially bottleneck nahi hai.

Verify: in-order ≈ 200 cyc; OoO ≈ 101 cyc; speedup . ✓ (Jab doosre cores involved hoon to cache coherence aur memory consistency ke saath interact karta hai.)


Forecast: I2 R1 padhta hai, I3 R1 overwrite karta hai — I3 pehle run karna safe hai?

  1. I2 aur I3 ke beech hazard identify karo. I2 reads R1; I3 writes R1. Write-After-Read = WAR = ek false dependency. Ye step kyun? WAR real data flow nahi hai — ye sirf kehta hai "I2 ke R1 padhne se pehle R1 mat chhedo."
  2. Renaming apply karo. I3 ki write ko ek fresh physical register P2 do (I1 ke P1 se alag jo I2 padhta hai). Ab I3 P2 likhta hai, I2 P1 padhta hai — koi conflict nahi. Ye step kyun? Renaming ordering constraint ko ek naming fact mein convert karta hai. Alag physical slots ke saath, execution order free hai.
  3. Conclusion: Haan, legal hai — renaming ke baad, I3 I2 se pehle execute ho sakta hai. Engine unhe abhi bhi program order mein commit karta hai (ROB, 1/cycle), to architectural R1 execution order se regardless I3 ki value hold karta hai.

Verify: hazard = WAR (false) → rename se removable → reorder legal. Contrast: agar I3 ne R1 padha hota (RAW), to reorder illegal hota. ✓


Recall Self-test (jawab dene ke baad reveal karo)

IPC ek sentence mein ::: instructions finished per clock cycle, — headline speed score Cell A binding ceiling ::: execution width (matched retire ke saath), commit ek settling cycle jodhta hai Cell B binding ceiling ::: critical path (dependency chain length) Ek bada window RAW chain kyun beat nahi kar sakta? ::: chain ek true data dependency hai — baad ke instructions ko literally pehle ke results chahiye WAR aur WAW hataye jaate hain ::: register renaming se (ye false / naming-only hain) Is page par P-prefixed naam ka matlab kya hai? ::: ek physical register — ek real CPU storage slot jo renamer ne diya (vs R = architectural register jo programmer ne likha) In examples mein ka matlab kya hai? ::: unlimited — pretend karo wo resource kabhi nahi khatam hota, taaki hum ek ceiling isolate kar sakein Example 4 total 5 kyun aur 4 nahi? ::: last instruction (I3) ka in-order commit uska result ready hone ke baad ek settling cycle jodhta hai Example 5 total 7 kyun aur 3 nahi? ::: chhah results burst hote hain lekin retire sirf 1/cycle drain karta hai — retire bandwidth bind karta hai Example 8 kaunsa hidden fourth ceiling unmask karta hai? ::: retire / commit bandwidth — sustained IPC R par saturate hota hai, kabhi infinite execution width par nahi I3 fault ke baad I4/I5 discardable kyun hain? ::: wo faulting head se younger hain; unke results sirf ROB mein rehte hain, kabhi committed nahi Cache miss ke dauran, OoOE kaam karte rehne ke liye kya karta hai? ::: independent kaam tak pahunchne ke liye kaafi bada instruction window Do independent misses ~100 nahi ~200 cycles isliye kyun lete hain ::: memory-level parallelism — dono requests ek saath outstanding hain