Memory Hierarchy & Caches
Level 5 — Mastery (cross-domain: architecture + math + coding + proof) Time limit: 90 minutes Total marks: 60
Instructions: Show all reasoning. Use / for mathematics. Byte addresses assume a byte-addressable memory unless stated otherwise. is base 2 for address-field derivations.
Question 1 — Cache geometry, miss classification & AMAT modelling (24 marks)
A single-core processor has a two-level cache hierarchy over a 32-bit byte-addressable physical address space.
- L1 (data): 32 KiB, 8-way set-associative, 64-byte lines, LRU, write-back + write-allocate. Hit time 1 cycle.
- L2 (unified): 512 KiB, 16-way set-associative, 64-byte lines. Local hit time 12 cycles.
- DRAM: 200-cycle access.
(a) For the L1 cache, derive the number of sets and the exact bit-widths of the offset, index, and tag fields. Show the arithmetic. (4)
(b) Repeat the field derivation for L2. Then compute the total tag storage overhead (in bits) of L1 alone, including one valid bit and one dirty bit per line. (4)
(c) Consider the loop below over double A[N] (8 bytes each), int N = 4096, arrays 64-byte aligned, and a cold cache:
for (int r = 0; r < 10; r++)
for (int i = 0; i < N; i++)
sum += A[i];Classify every L1 miss produced by this code into compulsory / capacity / conflict, giving the count of each type across the full run. Justify using the working-set size versus L1 capacity. (6)
(d) The system's L1 local miss rate is measured at 4% and L2 local miss rate at 25%. Using AMAT, compute the average memory access time in cycles. Then prove algebraically that halving the L1 miss rate yields a larger AMAT improvement than halving the L2 local miss rate, for these numbers, and state the general condition (in terms of the miss/penalty parameters) under which improving L1 dominates. (6)
(e) The designer proposes switching L1 to write-through + no-write-allocate. Give one workload characteristic that makes this better and one that makes it worse than the original policy, each justified by a concrete access-pattern argument. (4)
Question 2 — Virtual memory, TLB reach, and multi-level paging (18 marks)
A 64-bit machine uses 48-bit virtual addresses, 4 KiB pages, and a 4-level page table where each level indexes with 9 bits and each page-table entry (PTE) is 8 bytes.
(a) Verify that the address decomposition is consistent, and state the number of entries per page-table page and why it equals one 4 KiB frame's worth of PTEs. (3)
(b) A program touches a fully-populated contiguous 2 GiB region. Compute the total memory consumed by all levels of its page table (assume minimal but complete tables covering exactly that region). Show the count of tables at each level. (6)
(c) A 4-way set-associative TLB has 512 entries. Define TLB reach and compute it for 4 KiB pages. If the OS additionally supports 2 MiB huge pages, compute the reach when all TLB entries map huge pages, and state the miss-type consequence for a program with an 800 MiB working set. (5)
(d) Give the AMAT-style formula for effective memory access time with a TLB, given TLB hit rate , TLB access , a page-table walk cost (on TLB miss), and physical memory access (assume all pages resident, no cache). Then compute the effective time for , , , cycles. (4)
Question 3 — Cache coherence, MESI, and a consistency proof (18 marks)
Two cores (C0, C1) share memory via a snooping bus with write-back, write-allocate private L1 caches running the MESI protocol. Block starts in memory only (Invalid in both caches).
(a) Trace the MESI state of in both caches after each of the following operations, and note every bus transaction generated (e.g., BusRd, BusRdX, BusUpgr, Flush):
- C0 reads
- C1 reads
- C0 writes
- C1 writes
- C0 reads
Present as a 5-row table. (8)
(b) Explain precisely why the E (Exclusive) state exists as distinct from S (Shared): give a two-operation sequence where having E saves a bus transaction that a protocol with only M/S/I would incur. (4)
(c) Consider Dekker-style shared flags, initially x = y = 0:
Core 0: Core 1:
x = 1; y = 1;
r0 = y; r1 = x;
Prove that under sequential consistency the outcome is impossible, but show it is permitted under a store-buffer (TSO-like) relaxed model. Frame the impossibility as a contradiction over a total global order of the four memory operations. (6)
Answer keyMark scheme & solutions
Question 1
(a) L1 = 32 KiB = B. Line = 64 B offset bits. (1) Number of lines . Ways = 8 sets , index bits. (2) Tag bits. (1)
(b) L2 = 512 KiB = B. offset . Lines . Ways=16 sets , index . Tag bits. (2) L1 tag storage: 512 lines bits. (2)
(c) Working set B KiB exactly L1 capacity. Because A is contiguous and fully-associative-enough here (8-way, 64 sets, and 32 KiB fills all lines evenly with a linear stride), the whole array just fits. Lines spanned lines = exactly all 512 L1 lines. (2)
- Compulsory: first pass () touches each of the 512 distinct lines once for the first time 512 compulsory misses. (2)
- Capacity/Conflict: working set (32 KiB) capacity (32 KiB), and a linear scan maps the 512 lines onto 64 sets, 8 per set, exactly filling each set with no set over-subscribed under LRU. Hence on reuse passes the whole array is still resident 0 capacity, 0 conflict misses. Total misses . (2) (Note the array exactly equals capacity; had N been slightly larger, passes would thrash → capacity misses.)
(d) . (1) cycles. (2)
- Halve L1 miss to 2%: → improvement cycles.
- Halve L2 local miss to 12.5%: → improvement cycle. L1 halving () beats L2 halving (). (2) General condition: L1-rate halving improvement ; L2-rate halving improvement . L1 dominates iff , i.e. iff — always true here (the L1-miss penalty includes the full L2 access chain, so reducing L1 misses removes the L2 hit cost too). (1)
(e) Better: streaming/write-once workloads writing data never re-read soon (e.g. memset of large buffer): no-write-allocate avoids polluting L1 by fetching a line to be fully overwritten; write-through with a write buffer hides latency. (2)
Worse: workloads with high write locality / read-after-write reuse (e.g. accumulating into a hot variable in a tight loop): write-through generates a bus/L2 write on every store (bandwidth blowup) and no-allocate keeps the line out of L1, causing repeated misses. (2)
Question 2
(a) 4 KiB page offset bits. Index bits . ✓ Consistent. (1) Entries per PT page ; each PTE B B one frame. ✓ (2)
(b) 2 GiB B. Pages leaf pages (data). (1)
- L4 (PTE/leaf tables): each maps 512 pages tables. (1)
- L3: each maps 512 L4 tables tables. (1)
- L2: maps 2 L3 tables table. (1)
- L1 (root): table. (1) Total tables ; each KiB B MiB. (1)
(c) TLB reach = amount of memory mappable by the TLB's entries simultaneously (entries)(page size). (1) 4 KiB pages: MiB. (2) 2 MiB huge pages: MiB GiB. (1) An 800 MiB working set exceeds 2 MiB (base pages) → heavy TLB capacity misses; but MiB fits in huge-page reach → TLB misses become rare (compulsory only). (1)
(d) . (2) cycles. (2)
Question 3
(a) (8 marks; ~1.5 per correct row incl. bus txn)
| Op | Bus txn | C0 state | C1 state |
|---|---|---|---|
| C0 read B | BusRd (no sharer) | E | I |
| C1 read B | BusRd, C0 flushes/downgrades | S | S |
| C0 write B | BusUpgr (invalidate C1) | M | I |
| C1 write B | BusRdX, C0 Flush (writeback) then invalidate | I | M |
| C0 read B | BusRd, C1 Flush → both share | S | S |
(b) After a lone read that misses with no other cache holding the block, state E means the line is clean and privately owned. If the same core then writes it, it can silently transition with no bus transaction (no other copy to invalidate). A protocol with only M/S/I would have loaded it as S, forcing a BusUpgr/BusRdX on the write to invalidate phantom sharers. So E saves one bus transaction on the read-then-write sequence. (4)
(c) Under SC, all four ops (2 stores, 2 loads) appear in one total order consistent with each core's program order: before , and before . (2) Suppose . means read before in the global order. means read before . Program order: (last from reading old y) and . Chaining: — a cycle, contradicting a total order. Hence impossible. (2) Under TSO/store-buffer: each store sits in the core's local store buffer; the load and execute (bypass buffer / read memory) before the buffered stores drain to memory. Both loads see the old value 0. Store→load reordering is allowed (a load may complete before an earlier store in program order becomes globally visible), so is permitted. (2)
[
{"claim":"L1 tag=20 bits, sets=64, offset=6","code":"offset=log(64,2); sets=(2**15//2**6)//8; idx=log(sets,2); tag=32-offset-idx; result=(offset==6 and sets==64 and idx==6 and tag==20)"},
{"claim":"L1 tag storage = 11264 bits","code":"result=(512*(20+1+1)==11264)"},
{"claim":"AMAT baseline = 3.48 cycles","code":"amat=1+Rational(4,100)*(12+Rational(25,100)*200); result=(amat==Rational(348,100))"},
{"claim":"Halving L1 improves 1.24, halving L2 improves 1.00","code":"base=1+Rational(4,100)*62; a=1+Rational(2,100)*62; b=1+Rational(4,100)*(12+Rational(125,1000)*200); result=(base-a==Rational(124,100) and base-b==1 and (base-a)>(base-b))"},
{"claim":"Page table total = 1028 tables","code":"leaf=2**19//2**9; l3=leaf//512; l2=1; l1=1; result=(leaf==1024 and leaf+l3+l2+l1==1028)"},
{"claim":"TLB reach 2MiB (4K) and 1GiB (2M huge)","code":"r4=512*4096; rh=512*2*1024*1024; result=(r4==2*1024*1024 and rh==1024*1024*1024)"},
{"claim":"Effective time with TLB = 103 cycles","code":"result=(1+100+(1-Rational(98,100))*100==103)"}
]