<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Texas_DV_engineer</title>
        <link>https://velog.io/</link>
        <description>Design Verification engineer</description>
        <lastBuildDate>Wed, 04 Nov 2026 17:16:53 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Texas_DV_engineer</title>
            <url>https://velog.velcdn.com/images/houston_guy2/profile/96aebef2-f4b0-4cf0-94c8-d160da4da9e7/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Texas_DV_engineer. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/houston_guy2" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Multiprocessors]]></title>
            <link>https://velog.io/@houston_guy2/Multiprocessors</link>
            <guid>https://velog.io/@houston_guy2/Multiprocessors</guid>
            <pubDate>Wed, 04 Nov 2026 17:16:53 GMT</pubDate>
            <description><![CDATA[<p>Growth in data-intensive applications</p>
<ul>
<li>Data bases, file servers, …
Growing interest in servers, server perf.
Improved understanding in how to use multiprocessors effectively</li>
<li>Especially server where significant natural TLP
Advantage of leveraging design investment by replication</li>
<li>Rather than unique design</li>
</ul>
<p>“A parallel computer is a collection of processing elements that cooperate and communicate to solve large problems fast.”</p>
<p>Parallel Architecture = Computer Architecture + Communication Architecture</p>
<p>2 classes of multiprocessors WRT memory:</p>
<pre><code>1. Centralized Memory Multiprocessor
• &lt; few dozen processor chips (and &lt; 100 cores)
• Small enough to share single, centralized memory

2. Distributed Memory multiprocessor
• Larger number chips and cores than 1.
• BW demands -&gt; Memory distributed among processors</code></pre><h3 id="centralized-vs-distributed-memory">Centralized vs. Distributed Memory</h3>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f21cd459-24c7-4e0f-9333-cf28b707e614/image.png" alt=""></p>
<h3 id="centralized-memory-multiprocessor">Centralized Memory Multiprocessor</h3>
<p>Also called symmetric multiprocessors (SMPs) because single main memory has a symmetric relationship to all processors</p>
<p>Can scale to a few dozen processors by using a switch and by using many memory banks</p>
<p>Although scaling beyond that is technically conceivable, it becomes less attractive as the number of processors sharing centralized memory increases</p>
<h3 id="distributed-memory-multiprocessor">Distributed Memory Multiprocessor</h3>
<p>Pro: Cost-effective way to scale memory bandwidth
    • If most accesses are to local memory
Pro: Reduces latency of local memory accesses</p>
<p>Con: Communicating data between processors more complex</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TLB Exercise ]]></title>
            <link>https://velog.io/@houston_guy2/TLB-Exercise</link>
            <guid>https://velog.io/@houston_guy2/TLB-Exercise</guid>
            <pubDate>Wed, 04 Nov 2026 16:38:51 GMT</pubDate>
            <description><![CDATA[<p>풀이 순서 (공식 5개)</p>
<pre><code>① page offset 비트 = log₂(페이지 크기)
② VPN 비트       = 가상 주소 비트 − page offset
   PPN 비트       = 물리 주소 비트 − page offset
③ TLB index 비트  = log₂(TLB set 수),  TLB set 수 = 항목 수 ÷ way 수
   TLB tag 비트   = VPN − TLB index
④ 캐시 block offset = log₂(블록 크기)
   캐시 index       = log₂(set 수),  set 수 = 캐시 크기 ÷ 블록 크기 ÷ way 수
   캐시 tag         = 물리 주소 비트 − index − block offset
⑤ 비교: index + block offset ≤ page offset 이면 → 그 비트들은 번역 없이 가상 주소에서 바로 사용</code></pre><h1 id="exercise-1">Exercise 1</h1>
<p>Consider a system with the following processor components and policies:</p>
<ul>
<li>A direct-mapped L1 data cache of size 4KB and block size of 16 bytes, indexed and tagged using physical addresses, and using a write-allocate, write-back policy</li>
<li>A fully-associative data TLB with 4 entries and an LRU replacement policy</li>
<li>Physical addresses of 32 bits, and virtual addresses of 40 bits</li>
<li>Byte addressable memory</li>
<li>Page size of 1MB</li>
</ul>
<h2 id="1">(1)</h2>
<p>Which bits of the virtual address are used to obtain a virtual to physical translation from the TLB? Explain exactly how these bits are used to make the translation, assuming there is a TLB hit.</p>
<p>Exercise 1에 적용</p>
<p>주어진 값: 가상 주소 40비트, 물리 주소 32비트, 페이지 1 MB, TLB fully associative 4칸, 캐시 direct-mapped 4 KB, 블록 16 B</p>
<p>① page offset</p>
<pre><code>1 MB = 2²⁰ → page offset = 20비트</code></pre><p>② VPN, PPN</p>
<pre><code>VPN = 40 − 20 = 20비트
PPN = 32 − 20 = 12비트</code></pre><p>③ TLB 나누기</p>
<p>fully associative는 set이 1개(모든 칸이 한 set)라서:</p>
<pre><code>TLB index = log₂(1) = 0비트
TLB tag   = 20 − 0 = 20비트 (VPN 전부)</code></pre><p>④ 캐시 나누기</p>
<pre><code>block offset = log₂(16)          = 4비트
set 수       = 4 KB ÷ 16 B ÷ 1   = 256 → index = log₂(256) = 8비트
tag          = 32 − 8 − 4        = 20비트</code></pre><p>⑤ 번역 없이 쓸 수 있는 비트 찾기</p>
<p>page offset(하위 20비트)과 캐시 필드를 비트 번호로 겹쳐 봅니다.</p>
<pre><code>물리 주소 비트:  31 ........ 20 | 19 .... 12 | 11 .... 4 | 3 .. 0
page 기준:       [   PPN 12    |        page offset 20             ]
캐시 기준:       [          tag 20          |  index 8  | offset 4 ]</code></pre><ul>
<li>block offset (bit 3<del>0), index (bit 11</del>4) → page offset 안 → 가상 주소에서 바로</li>
<li>tag 하위 8비트 (bit 19~12) → page offset 안 → 가상 주소에서 바로</li>
<li>tag 상위 12비트 (bit 31~20) → PPN → 번역 후에만</li>
</ul>
<p>(1) TLB 번역에 쓰는 비트
page offset 계산 → 20비트 (번역 안 함)
나머지가 VPN → 상위 20비트 (bit 39~20)
TLB 구조로 VPN 나누기 → fully associative라 index 0, tag 20
hit 과정 서술: VPN 20비트를 4개 항목의 tag와 동시 비교 → 일치하는 valid 항목의 PPN(12비트) 읽기 → PPN + page offset = 물리 주소 32비트</p>
<p>The virtual address is 40 bits long. Because the virtual page size is 1MB = 2^20 bytes, and memory is byte addressable, the virtual page
offset is 20 bits. Thus, the first 40- 20=20 bits are used for address translation at the TLB. Since the TLB is fully associative, all of these bits are used for the tag; i.e., there are no index bits.
When a virtual address is presented for translation, the hardware first checks to see if the 20 bit tag is present in the TLB by comparing it to all other entries simultaneously. If a valid match is found (i.e., a TLB hit), the page frame number is read directly from the TLB.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/3237787b-dd8b-4eed-acfd-fbd5c68655ff/image.png" alt=""></p>
<hr>
<h2 id="2">(2)</h2>
<p>Which bits of the virtual or physical address are used as the tag, index, and block offset bits for accessing the L1 data cache? Explicitly specify which of these bits can be used directly from the virtual address without any translation</p>
<p>(2) 캐시의 tag, index, offset
캐시가 어느 주소를 쓰는지 확인 → “physically indexed and tagged”이니 물리 주소
block offset → index → tag 순서로 계산 (4, 8, 20)
비트 번호 적기 → offset 3<del>0, index 11</del>4, tag 31~12
page offset과 겹쳐 보기 → 하위 20비트는 가상 주소에서 바로, 상위 12비트만 번역 필요</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f9ecdb13-45e1-4e42-bdaa-81fa48889ebb/image.png" alt=""></p>
<p>Since the cache is physically indexed and physically tagged, all of the bits from accessing the cache must come from the physical address. However, since the lowest 20 bits of the virtual address form the page offset and are therefore not translated, these 20 bits can be used directly from the virtual address. The remaining 12 bits (of the total
of 32 bits in the physical address) must be used after translation. Since the block size is 16 bytes = 2^4 bytes, and memory is byte addressable, the lowest 4 bits are used as block offset. Since the cache is direct mapped, the number of sets is 4KB/16 bytes = 2^8. Therefore, 8 bits are needed for the index. The remaining 32-8-4 = 20 bits are needed for the tag. As mentioned above, the index and offset bits can be used before translation while the tag bits must await the translation for the 12 uppermost bits.</p>
<hr>
<h1 id="exercise-2">Exercise 2</h1>
<p>Consider a virtual memory system where a TLB access takes 4ns and there is a single level of 64KB, a 4-way set associative, 16-byte block, writeback data cache with the following parameters:</p>
<ul>
<li>indexing the cache to access the data portion takes 10ns</li>
<li>indexing the tag array of the data cache takes 8ns</li>
<li>tag comparisons take 3ns</li>
<li>multiplexing the output data takes 1ns</li>
</ul>
<p>Assume these are the only parts that affect the cache access time. Assume the page size is 4KB. Calculate the amount of time it takes to get data from the cache on a hit, including any needed TLB access time.</p>
<p>• If can access TLB and cache simulatenously Max( Max(TLB access, Tag index)+tag comparison, Data access) + Mux</p>
<p>• If cannot access TLB and cache concurrently TLB access + Max (Tag index + tag comparison, Data access) + Mux </p>
<hr>
<p>풀이 순서</p>
<pre><code>① 캐시 비트 계산: index + block offset
② page offset 계산
③ 비교: index + offset ≤ page offset 이면 병렬, 아니면 직렬
④ 맞는 공식에 시간 넣기</code></pre><p>① 캐시 index + block offset</p>
<pre><code>block offset = log₂(16 B) = 4비트
set 수       = 64 KB ÷ (4-way × 16 B) = 1024 → index = log₂(1024) = 10비트
합계         = 10 + 4 = 14비트</code></pre><p>② page offset</p>
<pre><code>페이지 4 KB = 2¹² → page offset = 12비트</code></pre><p>③ 비교</p>
<pre><code>캐시에 필요한 비트 14  &gt;  page offset 12</code></pre><p>index 비트 중 2비트가 page offset 밖(페이지 번호 쪽)에 있습니다. 이 2비트는 번역을 해야 알 수 있어서, TLB 번역이 끝나기 전에는 어느 set을 읽을지조차 정할 수 없습니다. 그래서 <strong>직렬(serial)</strong>로 접근해야 합니다.</p>
<p>④ 직렬 공식에 대입</p>
<pre><code>TLB access + max(tag index + tag comparison, data access) + mux
= 4 + max(8 + 3, 10) + 1
= 4 + max(11, 10) + 1
= 4 + 11 + 1
= 16 ns</code></pre><p>시간 순서로 보기</p>
<p>직렬(이 문제)과, 만약 병렬이 가능했다면 어떻게 되는지를 나란히 그리면 이렇습니다.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/e5b47a8b-a9b0-45f9-a890-74e7d4abc950/image.png" alt=""></p>
<h3 id="공식의-각-부분이-왜-그렇게-생겼나">공식의 각 부분이 왜 그렇게 생겼나</h3>
<h3 id="직렬-공식-tlb--maxtag-index--비교-data-index--mux">직렬 공식: TLB + max(tag index + 비교, data index) + mux</h3>
<ol>
<li>TLB (4 ns): index 비트 일부가 페이지 번호에서 오니까, 번역이 끝나야 캐시 접근을 시작할 수 있습니다.</li>
<li>tag 배열과 data 배열은 동시에 읽음: 같은 index로 두 배열을 같이 엽니다.<ul>
<li>tag 쪽: 읽기 8 ns + 비교 3 ns = 4 + 11 = 15 ns에 hit 신호</li>
<li>data 쪽: 읽기 10 ns = 4 + 10 = 14 ns에 데이터 준비</li>
</ul>
</li>
<li>max: mux는 둘 다 준비돼야 동작합니다. 4-way라서 4개 way의 데이터가 다 나와 있어야 하고, 그중 어느 way가 hit인지(비교 결과)도 알아야 고를 수 있기 때문입니다. 그래서 늦은 쪽(15 ns)을 기다립니다.</li>
<li>mux (1 ns): hit한 way의 데이터를 골라 내보냄 → 16 ns</li>
</ol>
<h3 id="병렬-공식-maxmaxtlb-tag-index--비교-data-index--mux">병렬 공식: max(max(TLB, tag index) + 비교, data index) + mux</h3>
<ul>
<li>tag 배열 읽기가 TLB와 동시에 시작됩니다.</li>
<li>그런데 tag 비교에는 TLB가 준 물리 페이지 번호가 필요하므로, 비교는 TLB와 tag 읽기 둘 다 끝난 뒤에 합니다 → max(TLB, tag index) + 비교</li>
<li>data 배열도 0부터 동시에 읽습니다.</li>
<li>mux는 hit 신호와 data가 둘 다 준비된 뒤 → max(…) + mux<pre><code>max(max(4, 8) + 3, 10) + 1 = max(11, 10) + 1 = 12 ns</code></pre></li>
</ul>
<p>병렬이 가능했다면 TLB 4 ns가 tag 읽기 8 ns 뒤에 숨겨져서 12 ns로 줄어듭니다.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/36e0cc34-eff1-4769-9efd-3dd70ddea7a2/image.png" alt=""></p>
<p>Solutions
Bits required for indexing = log (64KB/4*16) = 10. Bits required for block offset = 4. Total bits required to access the cache = 14. Total bits for the page offset = 12. So physical indexing cannot proceed before translation. The virtual address must be translated before anything else can happen. So the 4ns delay of the TLB access must be added into the total. Accessing the data and tag arrays occurs in parallel. The data values are available after 4+10=14ns but the tag hit signal does not appear until 4+8+3=15ns. At this point, the correct way can be selected from the mux, which has an additional 1ns delay. Therefore, the total access time is 16ns.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GPU]]></title>
            <link>https://velog.io/@houston_guy2/GPU</link>
            <guid>https://velog.io/@houston_guy2/GPU</guid>
            <pubDate>Mon, 02 Nov 2026 20:32:45 GMT</pubDate>
            <description><![CDATA[<p>cuda api is not in final exam</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Translation Lookaside Buffer (TLB)]]></title>
            <link>https://velog.io/@houston_guy2/Translation-Lookaside-Buffer-TLB</link>
            <guid>https://velog.io/@houston_guy2/Translation-Lookaside-Buffer-TLB</guid>
            <pubDate>Mon, 02 Nov 2026 16:58:10 GMT</pubDate>
            <description><![CDATA[<h1 id="translation-lookaside-buffer-tlb">Translation Lookaside Buffer (TLB)</h1>
<h2 id="1-why-we-need-translation-at-all">1. Why we need translation at all</h2>
<p>A TLB is a small, fast cache that remembers where virtual pages live in physical memory, so the CPU doesn’t have to read the page table from memory on every access.</p>
<ol>
<li>Why we need translation at all</li>
</ol>
<p>With virtual memory, every address a program uses is a virtual address. Before the CPU can fetch the data, it must turn that into a physical address (the real location in RAM).</p>
<p>An address is split into two parts:</p>
<pre><code>Virtual address  = [ virtual page number (VPN) | offset ]
Physical address = [ physical frame number (PFN) | offset ]</code></pre><ul>
<li>The offset (position inside the page) stays the same.</li>
<li>Only the page number changes, and the page table says what it changes to.</li>
</ul>
<p>Example (4 KB pages): virtual address 0x3ABC → VPN = 3, offset = 0xABC. The page table says “page 3 is in frame 7”, so the physical address is 0x7ABC.</p>
<h2 id="2-the-problem-two-memory-accesses-for-every-one">2. The problem: two memory accesses for every one</h2>
<p>The page table itself lives in main memory. So without help, every load or store needs:</p>
<ol>
<li>one memory access to read the page table entry (to find the frame), then </li>
<li>one memory access to get the actual data.</li>
</ol>
<p>That makes every memory access twice as slow.</p>
<h3 id="3-the-fix-the-tlb">3. The fix: the TLB</h3>
<p>The TLB (Translation Lookaside Buffer) is a tiny, very fast cache inside the CPU that stores recently used page table entries (VPN → frame number). It’s typically only tens to a few thousand entries, and often fully associative.</p>
<p>Analogy: the page table is the full address book in a drawer (main memory). The TLB is a sticky note on your monitor with the few addresses you’ve looked up recently. You check the sticky note first and only open the drawer if the address isn’t there.</p>
<h2 id="4-what-happens-on-each-access">4. What happens on each access</h2>
<p>There are four possible paths, from fastest to slowest:</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/c7a61cb5-bd1f-4b52-ad80-ac2234a44374/image.png" alt=""></p>
<p>Path 1, TLB hit + cache hit: The TLB gives the frame number immediately, the CPU forms the physical address, and the data is found in the cache. This is the common, fast case.</p>
<p>Path 2, TLB hit + cache miss: The translation is fast, but the data isn’t in the cache, so it comes from main memory.</p>
<p>Path 3, TLB miss, page is in memory: The translation isn’t on the “sticky note”, so the CPU (or OS) reads the page table in main memory. The page table says the page is in RAM, so its entry is copied into the TLB and the access is retried, now as a TLB hit.</p>
<p>Path 4, TLB miss, page is NOT in memory (page fault): The page table’s valid bit says the page is on disk. The OS must:</p>
<pre><code>1. load the page from disk into a free frame in memory,
2. update the page table,
3. update the TLB,
4. restart the instruction.</code></pre><p>Disk is extremely slow compared to memory, so this costs millions of cycles.</p>
<h2 id="5-slide-question-if-tlb-hit-where-can-you-find-the-data">5. Slide question: “If TLB hit, where can you find the data?”</h2>
<p>Answer: in the cache or in main memory, never on disk.</p>
<p>A TLB hit only tells you where the page is in physical memory. It says nothing about whether that particular block is in the cache.</p>
<ul>
<li>If the block is in the cache → cache (path 1)</li>
<li>If not → main memory (path 2)</li>
</ul>
<p>It can’t be on disk because the TLB only keeps entries for pages that are currently in physical memory. When the OS moves a page out to disk, it marks the page table entry invalid and removes (invalidates) that page’s TLB entry. So if the TLB has an entry, the page must be in RAM</p>
<ol start="6">
<li>Clearing up the slide’s wording: TLB miss ≠ page fault</li>
</ol>
<p>The slide jumps quickly from “TLB miss” to “page fault”, but they’re different events:</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f7948d3a-3e0a-4198-a91d-2c7439963871/image.png" alt=""></p>
<ul>
<li>Most TLB misses are not page faults. The page table usually says “the page is in memory, frame X”, and you just refill the TLB.</li>
<li>A page fault happens only when the page table says the page is not in memory.</li>
<li>The slide says the page table “may not be in the memory”. More precisely, it’s the page (the data) that may be on disk. The page table normally stays in memory.</li>
</ul>
<h2 id="7-summary-in-one-picture">7. Summary in one picture</h2>
<pre><code>Virtual address
   │
   ▼
 TLB ──hit──► physical address ──► cache ──miss──► main memory
   │
  miss
   ▼
 Page table (in memory)
   ├─ page in memory ──► update TLB ──► retry (now a TLB hit)
   └─ page NOT in memory ──► PAGE FAULT ──► OS loads page from disk
                                           ──► update page table + TLB ──► retry</code></pre><hr>
<p>Each virtual memory reference can cause two physical memory accesses</p>
<ul>
<li>One to fetch the page table</li>
<li>One to fetch the data</li>
</ul>
<p>To overcome this problem a high-speed cache is set up for page table entries</p>
<ul>
<li>Called a Translation Lookaside Buffer (TLB)</li>
<li>TLB is a cache of the page table</li>
<li>Contains page table entries that have been most recently used</li>
</ul>
<p>Given a virtual address, processor examines the TLB</p>
<p>If page table entry is present (TLB hit), the page frame address is retrieved and the real address is formed</p>
<ul>
<li>Page frame address: physical memory location of that page</li>
</ul>
<p>Question: if TLB hit, where can you find the data?
Cache? Memory? Disk?</p>
<p>If page table entry is not found in the TLB (TLB miss), the page number is used to index the process page table which may not in the memory</p>
<ul>
<li>Causes a page fault: page is not in the memory</li>
<li>Penalty: millions of cycles</li>
</ul>
<p>First checks if page is already in main memory</p>
<ul>
<li>If not in main memory a page fault is issued</li>
</ul>
<p>The TLB is updated to include the new page entry</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/b90b96c9-7733-43cc-a7b9-1f91ef0e032d/image.png" alt=""></p>
<h1 id="a-hirerachy-of-misses">A Hirerachy of Misses</h1>
<pre><code>1. TLB miss
2. Cache miss
3. Page fault
4. Multiple instances of any of these misses can occur for any single instruction</code></pre><h1 id="tlb-direct-mapped-vs-fully-associative">TLB (Direct mapped vs fully associative)</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/3970f971-06fa-4c2e-ba4b-3daad5d5214d/image.png" alt=""></p>
<p>Both sides translate the same virtual address, page 5, offset 502, to the same real address, frame 37, offset 502. They differ in how they find “page 5 → frame 37”.</p>
<h3 id="a-direct-mapping-the-page-table">(a) Direct mapping: the page table</h3>
<p>The page table has one entry for every virtual page, in order. So the page number is used directly as an index:</p>
<pre><code>page 5  →  go straight to entry #5  →  frame 37</code></pre><p>No searching is needed, because entry #5 always belongs to page 5. The entry doesn’t even need to store the page number.</p>
<h3 id="b-associative-mapping-the-tlb">(b) Associative mapping: the TLB</h3>
<p>The TLB is tiny and holds only a few recently used pages, in any order (19, 511, 37, 27, 14, 1, 211, 5, 90, …). Page 5 could be in any slot, so:</p>
<ul>
<li>each TLB entry stores the page number (its tag) next to the page table entry, and </li>
<li>the incoming page number (5) is compared against all entries at the same time.</li>
</ul>
<p>The entry tagged 5 matches, and it gives frame 37. This is exactly a fully associative cache, where the page number is the tag.</p>
<p>Then: real address = frame number + same offset</p>
<pre><code>Virtual:  [ page 5  | offset 502 ]
Real:     [ frame 37 | offset 502 ]    ← offset is copied unchanged</code></pre><p>Why the TLB is usually (fully) associative</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/465e2edf-39ee-4171-a998-19ab8564bc32/image.png" alt="">
TLB misses are expensive (a page table walk), and the TLB is small, so having one comparator per entry is affordable. That’s why TLBs are typically fully associative or highly set associative. It’s the same reasoning as the victim cache.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/609813c5-520f-4dbf-ba7c-a5dcefdc110a/image.png" alt=""></p>
<p>Any problem with this design w.r.t performance ?</p>
<h1 id="tlb-access-timing">TLB Access Timing</h1>
<p>Don’t want TLB in critical path to cache</p>
<ul>
<li>But, TLBs are early select -- have to match page name before accessing memory address</li>
<li>Slow approach is:
  TLB page name -&gt; TLB Page Frame Address -&gt; Cache Access</li>
</ul>
<p>The problem: the slow, serial path</p>
<p>The cache in this figure is physically addressed: its tags are physical addresses. So the obvious way to access it is:</p>
<pre><code>TLB (translate page #) → get frame # → THEN access the cache
hit time = TLB time + cache time</code></pre><p>The TLB lookup sits in the critical path of every load and store, which makes every cache hit slower.</p>
<p>The key observation: the offset bits never change</p>
<p>Translation only changes the page number. The page offset (low 12 bits for a 4 KB page) is identical in the virtual and physical address. So the CPU can start using those bits before translation finishes.</p>
<p>The solution: access the TLB and the cache at the same time</p>
<p>Here is how the figure’s numbers fit together:</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/5c1f1f92-7d31-4516-877c-c40b113115a4/image.png" alt=""></p>
<p>Step by step, for one access:</p>
<ol>
<li>At the same time:<ul>
<li>the 20 high bits (virtual page number) go to the TLB, and</li>
<li>the 12 low bits (page offset) go straight to the 4 KB cache to select the block (“block select”). The cache reads out that block’s data and its stored 18-bit tag.</li>
</ul>
</li>
<li>The TLB outputs the 18-bit page frame address (the physical page number).</li>
<li>Compare the TLB’s frame number with the cache’s tag: hit if they match.</li>
</ol>
<p>The numbers in the figure:</p>
<ul>
<li>4 KB pages → page offset = log₂(4096) = 12 bits, so VPN = 32 − 12 = 20 bits</li>
<li>4 KB direct-mapped cache → index + block offset = log₂(4096) = 12 bits, exactly the page offset</li>
<li>Physical address = 18-bit frame + 12-bit offset = 30 bits (1 GB of physical memory), so the cache tag = 30 − 12 = 18 bits</li>
</ul>
<p>This design is called virtually indexed, physically tagged (VIPT): the cache is indexed with bits taken from the virtual address, but compared against physical tags.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/34146a24-7256-4d4e-a2cb-237c1e044f46/image.png" alt=""></p>
<p>“Any requirements to use this method?” — Yes</p>
<p>The cache’s index bits and block offset bits must all come from the page offset, the bits that don’t change during translation:</p>
<pre><code>index bits + block offset bits ≤ page offset bits
⇔  cache size ÷ associativity ≤ page size</code></pre><p>In other words, one way of the cache must be no bigger than one page.</p>
<p>Why: if even one index bit came from the virtual page number, that bit could be different in the physical address. You’d then read the wrong set before knowing the translation. A related problem: two virtual addresses that map to the same physical address (aliases) could land in different sets, giving two copies of the same data.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/d413400c-0189-45c8-93f1-e9faf4488110/image.png" alt=""></p>
<p>How to get a bigger L1 and keep the trick</p>
<ul>
<li>Raise associativity: a bigger cache with more ways keeps each way at 4 KB. That’s why many L1 caches are 32 KB 8-way with 4 KB pages.</li>
<li>Use bigger pages: the page offset gets more bits.</li>
<li>Add hardware or OS support for aliases (for example, page coloring, where the OS makes sure the extra index bits are the same in the virtual and physical address).</li>
</ul>
<p>Solution: Access TLB &amp; cache concurrently
Any requirements to use this method?
<img src="https://velog.velcdn.com/images/houston_guy2/post/4260f77d-b931-4236-a6e0-1f92e0c77353/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/605f4640-6383-471f-a4fa-9564683e38cf/image.png" alt=""></p>
<h1 id="an-memory-hierarchy-example">An Memory Hierarchy Example</h1>
<ul>
<li>Page size 8KB</li>
<li>TLB direct mapped with 256 entries</li>
<li>L1 cache direct-mapped, 8KB</li>
<li>L2 cache direct-mapped, 4MB</li>
<li>Both use 64-byte blocks</li>
<li>Virtual address 64 bits, physical address 41 bits</li>
<li>Draw the figure of the memory hierarchy from virtual address to L2 cache access.</li>
</ul>
<p>you should know how to draw below structure</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/ea5989a4-d5e4-4cc2-a058-2350ffaf50ea/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/21027cc7-d3b8-4199-aa6d-0099592a948c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/ac59bfc1-666f-4c89-8857-dd214ba73f1f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/70497c3a-f31c-4c9d-873f-089978b1b817/image.png" alt=""></p>
<p>검산: 합이 주소 길이와 맞는지</p>
<pre><code>가상 주소: TLB tag 43 + TLB index 8 + page offset 13 = 64 ✓
물리 주소 (L1 기준): L1 tag 28 + index 7 + offset 6 = 41 ✓
물리 주소 (L2 기준): L2 tag 19 + index 16 + offset 6 = 41 ✓</code></pre><h2 id="2단계-그림의-흐름">2단계: 그림의 흐름</h2>
<h3 id="①-가상-주소를-둘로-나눔">① 가상 주소를 둘로 나눔</h3>
<p>64비트 = 가상 페이지 번호 51 | page offset 13</p>
<h3 id="②-tlb와-l1을-동시에-찾음-지난번에-배운-parallel-access-vipt">② TLB와 L1을 동시에 찾음 (지난번에 배운 parallel access, VIPT)</h3>
<p>TLB 쪽: 페이지 번호 51비트 = TLB tag 43 | TLB index 8. index 8비트로 TLB 칸 하나를 고르고, 저장된 TLB tag(43)와 비교합니다 (왼쪽 =?). 같으면 TLB hit이고, 그 칸의 TLB data 28비트 = 물리 페이지 번호가 나옵니다.
L1 쪽 (동시에): page offset 13비트 = L1 index 7 | block offset 6. 번역이 필요 없는 비트라 바로 L1 칸을 골라 <strong>L1 tag(28)와 data(512)</strong>를 읽습니다.</p>
<h3 id="③-l1-hit-판단">③ L1 hit 판단</h3>
<p>TLB가 준 물리 페이지 번호 28비트(“L1 tag compare address”)와 L1에서 읽은 tag 28비트를 비교합니다 (가운데 =?). 같으면 L1 hit → block offset으로 원하는 워드를 골라 CPU로 보냅니다.</p>
<h3 id="④-l1-miss면-물리-주소를-만들어-l2로">④ L1 miss면 물리 주소를 만들어 L2로</h3>
<p>물리 주소 41비트 = 물리 페이지 번호 28 (TLB에서) | page offset 13 (원래 주소에서). 이걸 L2 기준으로 다시 나눕니다: L2 tag 19 | L2 index 16 | block offset 6</p>
<h3 id="⑤-l2-접근">⑤ L2 접근</h3>
<p>index 16비트로 L2 칸을 고르고, 저장된 tag(19)와 비교합니다 (아래 =?). 같으면 L2 hit → 데이터를 L1이나 CPU로 보냅니다.</p>
<h3 id="왜-l1-tag를-43에서-28로-고쳤나">왜 L1 tag를 43에서 28로 고쳤나</h3>
<p>교과서 원래 그림에는 L1 cache tag &lt;43&gt;로 나와 있는데, 28이 맞습니다 (그래서 고쳐 쓰신 거예요).</p>
<p>L1 tag는 TLB가 준 <strong>물리 페이지 번호(28비트)</strong>와 비교됩니다. 비교하는 두 값의 길이가 같아야 하니 28비트입니다.
계산으로도 물리 주소 41 − index 7 − offset 6 = 28입니다.</p>
<p>43은 TLB tag 길이와 헷갈린 오타입니다.</p>
<p>왜 L1은 TLB와 동시에, L2는 번역 후에 접근하나</p>
<p>지난번 규칙을 적용하면:</p>
<pre><code>동시 접근 조건: index 비트 + block offset 비트 ≤ page offset 비트</code></pre><p><img src="https://velog.velcdn.com/images/houston_guy2/post/fbf72f9c-8307-4488-b97c-21a840d7d68a/image.png" alt="">
그래서 L1은 VIPT(가상 주소로 index, 물리 주소로 tag 비교)이고, L2는 번역이 끝난 물리 주소로만 접근합니다 (PIPT). L2는 L1 미스 뒤에야 접근하니, 그때는 이미 번역이 끝나 있어서 기다릴 일이 없습니다.</p>
<h2 id="시험에서-그리는-순서">시험에서 그리는 순서</h2>
<ol>
<li>가상 주소 → VPN | page offset (offset = log₂ 페이지 크기)</li>
<li>VPN → TLB tag | TLB index (index = log₂ TLB 칸 수, direct-mapped 기준)</li>
<li>TLB data = 물리 주소 − page offset</li>
<li>page offset → L1 index | block offset, L1 tag = 물리 주소 − index − offset</li>
<li>물리 주소 = TLB data | page offset</li>
<li>물리 주소 → L2 tag | L2 index | block offset</li>
<li>비교기 3개: TLB tag, L1 tag, L2 tag</li>
<li>마지막으로 합이 64 / 41인지 검산</li>
</ol>
<p>시험에서 숫자가 바뀌어도 이 순서대로 log₂만 하면 됩니다. 예를 들어 TLB가 4-way 256칸이면 TLB index = log₂(256 ÷ 4) = 6, TLB tag = 51 − 6 = 45가 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HW4]]></title>
            <link>https://velog.io/@houston_guy2/HW4-59pitiy5</link>
            <guid>https://velog.io/@houston_guy2/HW4-59pitiy5</guid>
            <pubDate>Mon, 02 Nov 2026 05:18:29 GMT</pubDate>
            <description><![CDATA[<h1 id="1">1</h1>
<p>Consider the following code, which sums the elements of a product of two matrices:</p>
<pre><code class="language-c">register int i, k; 
// i, k are in the processor registers 
register float sum, a[8][8], b[8][8];

for(i = 0; i &lt; 8; i++) { /* 1 */
    for(k = 0; k &lt; 8; k++) { /* 2 */
        sum += a[i][k] * b[k][i]; /* 3 */
    }
 }</code></pre>
<p>Assume the following:</p>
<ul>
<li>There is a perfect instruction cache; i.e., do not worry about the time for any instruction accesses.</li>
<li>Both int and float are of size 4 bytes.</li>
<li>Assume that only the accesses to the arrays a and b generate accesses to the data cache. The rest of the variables are all allocated in registers.</li>
<li><span style="color:orange">Assume a fully associative, LRU data cache with 8 lines, where each line is 32 bytes.</span></li>
<li>Initially, the data cache is empty.</li>
<li>The arrays a and b are stored in row order.</li>
<li>To keep things simple, we will assume that statements in the above code are executed sequentially.
Lines (1), and (2) take 10 cycles for each invocation. Line (3) takes 10 cycles plus an additional 20 cycles per data cache miss to wait for the data. That is, if both array accesses in line (3) miss, it takes a total of 50 cycles.</li>
<li>Assume that the arrays a and b both start at cache line boundaries</li>
</ul>
<p>--</p>
<p>먼저 볼 것: 한 행(row) = 캐시 한 줄
float 하나 = 4 B, 한 행 = 8개 × 4 B = 32 B = 캐시 한 줄
배열은 행 우선(row order)으로 저장되고 줄 경계에서 시작하므로:
a[i][0..7] → 줄 A_i 하나
b[k][0..7] → 줄 B_k 하나
a, b 각각 8줄씩 총 16줄인데, 캐시는 8줄뿐입니다.</p>
<p>접근 패턴
i를 하나 고정하고 k = 0..7을 돌면:</p>
<pre><code>a[i][k] : a[i][0], a[i][1], ..., a[i][7]  → 전부 같은 줄 A_i (행 방향)
b[k][i] : b[0][i], b[1][i], ..., b[7][i]  → B0, B1, ..., B7 (열 방향 = 매번 다른 줄)</code></pre><p>i 하나를 처리하는 데 필요한 줄은 A_i 1개 + B0~B7 8개 = 9줄인데, 캐시는 8줄입니다. 여기서 문제가 생깁니다.
-&gt; 문제에서 cache line = 8 lines 이라고 했음</p>
<h2 id="a">(a)</h2>
<p>How many accesses to arrays a and b will result in cache misses? Explain your answer.</p>
<p>답은 (a) 72번 미스 (a 8번 + b 64번).</p>
<p>(a) 미스 횟수 = 72
i = 0</p>
<p>접근 순서: A0, B0, A0, B1, A0, B2, …, A0, B7</p>
<p>A0: 처음 1번 미스, 나머지 7번 hit
B0~B7: 처음이라 8번 모두 미스
B7이 들어올 때 캐시가 꽉 차 있으므로 LRU인 B0이 쫓겨남</p>
<p>i = 0이 끝난 뒤 캐시 (오래된 것 → 최근 것): B1 B2 B3 B4 B5 B6 A0 B7</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/08738bb8-5a3e-4e3b-bb3e-9406b6186713/image.png" alt=""></p>
<p>B 줄들은 순서대로 돌아가며 쓰이는데, LRU는 <strong>”가장 오래전에 쓴 줄&quot;</strong>을 내보내고, 그게 바로 곧 다시 쓸 줄입니다. 9줄이 8칸 캐시를 순환하면 LRU에서는 b 접근이 전부 미스가 납니다. A_i는 매 반복 쓰이니 계속 남아 있습니다.</p>
<p>끝난 상태가 i = 0 뒤와 같은 모양이라, i = 2~7도 똑같이 반복됩니다</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/c9329e8c-9787-434f-8502-b6e70a44f4b9/image.png" alt=""></p>
<p>참고로 실행 시간은 10 × 8 (line 1) + 10 × 64 (line 2) + 10 × 64 (line 3) + 20 × 72 (미스) = 2800 cycles입니다.</p>
<h2 id="b">(b)</h2>
<p>Now assume there is a data prefetch instruction with the format prefetch (array[index1][index2]). This prefetches the entire block containing the word array[index1][index2] into the data cache.</p>
<p>It takes <strong>1 cycle</strong> for the processor to execute this instruction and send it to the data cache. The processor can then go ahead and execute subsequent instructions. If the prefetched data is not in the cache, it takes <strong>20 cycles</strong> for the data to get loaded into the cache. </p>
<p><strong>Add prefetch instructions to minimize the execution time.</strong> Do not transform the code in any other way. <strong>How many cache misses for accessing a and b at line (3) in your modified code?</strong></p>
<p>(Hint: since line 1, 2, and 3 each takes 10 cycles when there is no cache miss, you can consider using them to hide the 20-cycle latency for the data to get loaded into the cache when prefetching. If you insert the prefetch instructions appropriately, the cache misses can be totally eliminated.)</p>
<p>(b) Prefetch로 미스 0번 만들기
핵심: 데이터가 필요한 시점보다 20 cycles 이상 먼저 prefetch</p>
<p>prefetch는 발행에 1 cycle, 데이터 도착에 20 cycles가 걸립니다. 힌트대로 line 1, 2, 3이 각각 10 cycles이므로 두 줄(20 cycles)만큼 앞서 prefetch하면 지연이 다 숨겨집니다</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/1d1e8ddd-a3e1-4fb3-aff4-44416dcaed14/image.png" alt=""></p>
<pre><code class="language-c">prefetch(a[0][0]);                   /* A0: 첫 a 줄 */
prefetch(b[0][0]);                   /* B0: 첫 b 줄 */
for (i = 0; i &lt; 8; i++) {            /* 1 */
    for (k = 0; k &lt; 8; k++) {        /* 2 */
        prefetch(b[k+1][i]);         /* 다음 반복에 쓸 b 줄 B_(k+1) */
        sum += a[i][k] * b[k][i];    /* 3 */
    }
    prefetch(a[i+1][0]);             /* 다음 i의 a 줄 A_(i+1) */
    prefetch(b[0][i+1]);             /* 다음 i의 첫 b 줄 = B0 */
}</code></pre>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/6bc52898-abbd-478a-8865-f083d9a19219/image.png" alt=""></p>
<p>각 prefetch가 왜 그 위치인가</p>
<p>① 루프 전 prefetch 2개
prefetch 두 개(2 cycles)를 낸 뒤 line 1(10) + line 2(10) = 20 cycles가 지나고 첫 line 3이 실행되므로, 딱 맞게 도착합니다.</p>
<p>② 안쪽 루프의 prefetch(b[k+1][i])
반복 k에서 발행하면, 그 뒤 line 3(10) + 다음 반복의 line 2(10) = 20 cycles 후에 b[k+1][i]를 씁니다. 한 반복 앞서 가져오면 충분합니다.</p>
<p>③ 바깥 루프 끝의 prefetch 2개
k = 7의 line 3이 끝나고 다음 i의 첫 line 3까지 line 1(10) + line 2(10) = 20 cycles입니다.</p>
<p>a[i+1][0]: 다음 행의 a 줄
b[0][i+1]: 줄 B0. b[0][어떤 열]이든 같은 줄이라, 다음 i의 첫 b 접근 b[0][i+1]이 미리 들어옵니다.
a는 왜 한 행에 한 번만 prefetch하나</p>
<p>a[i][0..7]이 전부 한 줄(A_i)이라, 첫 접근만 미스입니다. 그 뒤로는 매 반복 쓰이니 LRU에서 쫓겨나지 않습니다. 그래서 행마다 한 번만 가져오면 됩니다.</p>
<p>주의: 너무 일찍 prefetch하면 안 됨</p>
<p>prefetch(a[i+1][0])을 바깥 루프 처음(안쪽 루프 전)에 두면, 실제로 쓰기 전까지 A_i와 B0~B7, 9개 줄이 쓰입니다. A_(i+1)은 그동안 한 번도 안 쓰여서 LRU가 내보냅니다. 그러면 다시 미스가 나므로, 쓰기 직전 20 cycles 시점에 가져와야 합니다.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/d27f8328-acef-4019-b0c1-6a37af4bff52/image.png" alt=""></p>
<p>(b) 답: line 3에서 a, b 접근 미스 0번</p>
<p>참고: 쓸모없는 prefetch 몇 개
k = 7일 때 b[8][i]는 배열 b 바깥 주소이고, 다음에 필요한 B0는 ③에서 이미 가져옵니다.
i = 7일 때 a[8][0], b[0][8]도 더 이상 필요 없는 데이터입니다.</p>
<hr>
<h1 id="2">2</h1>
<p>Consider a virtual memory system where a TLB access takes 6ns and there is a single level of 32KB, a 4-way set associative, 16-byte block, write-back data cache with the following parameters:</p>
<p>• Indexing the cache to access the data portion takes 12ns
• Indexing the tag array of the data cache takes 8ns
• Tag comparisons take 3ns
• Multiplexing the output data takes 1ns</p>
<p>Assume these are the only parts that affect the cache access time. Assume the page size is 8KB. Calculate the amount of time it takes to get data from the cache on a hit, including any needed TLB access time. Please explain your answers in detail to get the full credits. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Virtual Memory]]></title>
            <link>https://velog.io/@houston_guy2/Virtual-Memory</link>
            <guid>https://velog.io/@houston_guy2/Virtual-Memory</guid>
            <pubDate>Wed, 28 Oct 2026 16:01:04 GMT</pubDate>
            <description><![CDATA[<h1 id="virtual-memory">Virtual Memory</h1>
<p>각 프로그램은 자기 혼자 메모리 전체를 쓰는 것처럼 보이는 가짜 주소(가상 주소)를 써요. 하드웨어와 OS가 이 주소를 실제 메모리 위치(물리 주소)로 바꿔 줘요.</p>
<p>비유하면 이래요. 프로그램은 &quot;내 방 101호&quot;라고만 알고 있고, 그 방이 건물 어디에 있는지는 관리인(OS)이 장부(페이지 테이블)를 보고 찾아 줘요. 다른 프로그램도 “101호”를 쓰지만, 실제로는 서로 다른 방이에요.</p>
<h1 id="1-the-limits-of-physical-addressing">1. The Limits of Physical Addressing</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f7fd02ee-5caa-4861-ba79-8b5080017d45/image.png" alt=""></p>
<p>물리 주소를 그대로 쓰면 생기는 문제</p>
<ul>
<li>모든 프로그램이 같은 메모리 공간을 같이 써요. 그래서 다른 프로그램의 데이터를 덮어쓸 수 있어요.</li>
<li>프로그램이 컴퓨터의 메모리 구조를 알고 있어야 해요. 어느 주소가 비어 있는지 알아야 하니까요.</li>
<li>프로그램이 실제 메모리보다 클 수 없어요.</li>
</ul>
<h1 id="2-evolution-of-virtual-memory">2. Evolution of Virtual Memory</h1>
<h3 id="1-originally----one-computer-one-program">1. Originally -- one computer; one program</h3>
<p>① 컴퓨터 한 대에 프로그램 하나
한 프로그램이 컴퓨터를 통째로 써요.
남는 메모리는 낭비되고, I/O를 기다리는 동안 CPU는 놀아요.
<img src="https://velog.velcdn.com/images/houston_guy2/post/119adcdb-aab2-4e7c-9e8c-e539def2095a/image.png" alt=""></p>
<h3 id="2-one-computer-several-programs">2. one computer, several programs</h3>
<p>컴퓨터 한 대에 프로그램 여러 개
-한 프로그램이 I/O를 기다리는 동안 다른 프로그램을 돌려서 CPU를 계속 일하게 해요. 새로운 문제는 단편화(fragmentation) 예요.</p>
<pre><code>메모리:  [Job1][빈 10KB][Job2][빈 15KB][Job3][빈 10KB]
                ↑                ↑               ↑
         빈 공간 합계 = 35KB인데...

Job 5 (30KB 필요) → 연속된 30KB가 없어서 못 들어감!</code></pre><p>빈 공간이 충분해도 여기저기 쪼개져 있으면 못 써요. 프로그램은 연속된 공간이 필요하니까요.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/a9459f18-23f2-4053-b1f8-6b78f6fc0453/image.png" alt=""></p>
<h3 id="3-base--bound-레지스터">3. Base / Bound 레지스터</h3>
<ul>
<li>Base: 프로그램이 쓰는 모든 주소에 base 값을 더해요. 그래서 프로그램은 항상 0번지부터 시작한다고 생각하면 되고, 실제 위치는 base만 바꾸면 옮길 수 있어요.</li>
<li>Bound: 프로그램이 쓸 수 있는 크기예요. 이 범위를 넘으면 예외(exception)를 일으켜서 다른 프로그램을 보호해요.<pre><code>물리 주소 = Base + 프로그램이 쓴 주소
조건:      프로그램이 쓴 주소 &lt; Bound  (넘으면 예외)</code></pre><img src="https://velog.velcdn.com/images/houston_guy2/post/3a99b865-709e-487a-95be-bc9c9298db0b/image.png" alt=""></li>
<li>단편화는 압축(compaction) 으로 해결해요. 프로그램들을 한쪽으로 밀어서 빈 공간을 하나로 모으는 거예요. base만 바꾸면 되니까 프로그램은 옮겨진 걸 몰라요.</li>
</ul>
<h3 id="4-그래도-남는-문제">4. 그래도 남는 문제</h3>
<ul>
<li>압축할 때 메모리를 통째로 복사해야 해서 느려요.</li>
<li>프로그램이 실제 메모리보다 크면, 프로그래머가 직접 쪼개서 필요할 때 넣었다 뺐다 해야 해요. 이걸 오버레이(overlay) 라고 해요. 엄청 귀찮고 실수하기 쉬워요.</li>
</ul>
<h1 id="3-solution-add-a-layer-of-indirection">3. Solution: Add a Layer of Indirection</h1>
<p>해결책: 가상 메모리 = &quot;중간에 한 단계 더 끼워 넣기&quot;
프로그램은 가상 주소만 쓰고, 중간의 주소 변환(address translation) 이 실제 위치를 찾아 줘요.</p>
<ul>
<li>User programs run in an virtual address space</li>
<li>Address Translation maps virtual address to physical memory</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/44728fb1-7c1e-409a-b2e8-bdf6bd8b07f9/image.png" alt=""></p>
<h3 id="장점">장점</h3>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/511c0162-0e3b-4471-b97c-553e435a88cc/image.png" alt=""></p>
<p>Address Renaming in the Memory Hierarchy
<img src="https://velog.velcdn.com/images/houston_guy2/post/b4fe2c17-1bba-4a4f-b86a-005ad5412fd1/image.png" alt=""></p>
<p>Programmer sees address hierarchy:</p>
<ul>
<li>Registers</li>
<li>“Memory”</li>
<li>Files</li>
<li>Messages</li>
</ul>
<p>Machine hides hardware details:</p>
<ul>
<li>Virtual to physical memory mapping</li>
<li>File / virtual address to disk sector # mapping</li>
<li>Messaging destination to IP address/routing information</li>
</ul>
<h1 id="4-page-고정크기로-자르기">4. Page 고정크기로 자르기</h1>
<p>메모리를 같은 크기의 조각(페이지, 보통 4KB) 으로 잘라요.</p>
<p>Virtual Memory uses Mapped Pages</p>
<ul>
<li>Memory space is treated as a set of pages that may be arbitrarily distributed among main memory and swap space</li>
<li>Fixed size pages eliminate memory fragmentation</li>
<li>No need for compacting and cost of copying memory blocks</li>
<li>Base+Bound registers replaced with mapping tables (page tables)</li>
</ul>
<pre><code>가상 메모리 (프로그램이 보는 것)      물리 메모리 (실제)
  페이지 0  ──────────────┐         ┌── 프레임 0: 다른 프로그램
  페이지 1  ────────┐     │         │   프레임 1: 페이지 1 ◀─┘
  페이지 2  ──┐     │     └────────▶│   프레임 2: 페이지 0
  페이지 3  ─ │ ─ ─ │ ─ ─ ─ ─ ▶ 디스크 (swap)
              └─────│────────────▶│   프레임 3: 페이지 2</code></pre><p>가상으로는 연속이지만, 실제로는 아무 데나 흩어져 있어도 돼요.
크기가 다 같으니까 빈 칸 아무 데나 넣으면 돼요. 그래서 단편화가 없고 압축도 필요 없어요.
base/bound 레지스터 대신 페이지 테이블이 &quot;페이지 몇 번이 어디 있나&quot;를 기록해요.</p>
<h1 id="5-four-memory-hierarchy-question-for-virtual-memory">5. Four Memory Hierarchy Question for Virtual Memory</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/9f09690d-fda8-46b5-a149-02fa80c72275/image.png" alt=""></p>
<h1 id="6-format-of-page-table-entry">6. Format of Page Table Entry</h1>
<p>페이지 하나당 한 줄씩 있어요.</p>
<pre><code>┌────────────────────┬───────┬───────┬────────────┬────────────┐
│ 주소 (물리 프레임      │ Valid │ Dirty │ Referenced │ Protection │
│  번호 or 디스크 주소)  │       │       │            │            │
└────────────────────┴───────┴───────┴────────────┴────────────┘</code></pre><p><img src="https://velog.velcdn.com/images/houston_guy2/post/3f81ddad-be77-43f0-8258-a5294cc7fd58/image.png" alt=""></p>
<h1 id="7주소-변환-방법">7.주소 변환 방법</h1>
<ul>
<li>Pointer to location of page in memory</li>
<li>Or, if page is swapped, can be disk address instead</li>
</ul>
<pre><code>가상 주소:  [  가상 페이지 번호 (VPN)  |  오프셋 (offset)  ]
                       │                      │
                 페이지 테이블에서              그대로 복사
                 찾아서 바꿈                     │
                       ▼                      ▼
물리 주소:  [  물리 프레임 번호 (PFN)  |  오프셋 (offset)  ]</code></pre><ul>
<li>오프셋: 페이지 안에서의 위치. 페이지 크기로 결정돼요. 4KB = 2^12 → 12비트</li>
<li>VPN: 나머지 비트. 이걸 페이지 테이블에서 찾아서 물리 프레임 번호로 바꿔요.</li>
</ul>
<h4 id="control-bits">Control bits</h4>
<ul>
<li>Valid/Present bit
  » If set, page being pointed to is resident in memory</li>
<li>Modified/dirty bit
  » Set if at least one word in page has been modified
  » If not, don’t need to write to disk when replaced</li>
<li>Referenced bit
  » Set if page has been referenced (with either read or write)
  » Used to support software replacement policies</li>
<li>Protection bits
  » Used to restrict access
  » For example, read-only access, or system-only access</li>
</ul>
<p>1-Level Direct Page Table Translation</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/6ba95cbe-a552-430e-ac70-703a543b47d5/image.png" alt=""></p>
<h2 id="1-level-table-simple-but-limited">1-Level Table Simple, but Limited</h2>
<p>For large virtual address spaces can grow very large</p>
<ul>
<li>lpha 21164 has 43-bit virtual address, how many page table entries? Assume each entry is 8B, the page size is 8KB.</li>
<li>Impractical today -- that’s a substantial part of a disk drive just for the page table</li>
<li>Multi-level page tables</li>
</ul>
<p>계산 공식 (시험에 자주 나와요)</p>
<pre><code>오프셋 비트 수   = log2(페이지 크기)
VPN 비트 수     = 가상 주소 비트 수 − 오프셋 비트 수
PTE 개수        = 2^(VPN 비트 수)
페이지 테이블 크기 = PTE 개수 × PTE 하나 크기</code></pre><p>글에 있는 문제: Alpha 21164 (43비트 가상 주소, 8KB 페이지, PTE 8B)</p>
<pre><code>오프셋 = log2(8KB) = 13비트
VPN   = 43 − 13 = 30비트
PTE 개수 = 2^30 ≈ 10억 개
테이블 크기 = 2^30 × 8B = 2^33 B = 8 GB  ← 프로그램 하나당!</code></pre><p>프로그램 하나 돌리려고 페이지 테이블에만 8GB가 필요해요. 말이 안 되죠. 그래서 다음 방법이 필요해요.</p>
<h1 id="8-2-level-page-table">8. 2-Level Page Table</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/14a7642a-d3b5-481d-bcba-bef02f007fb9/image.png" alt=""></p>
<p>다단계(Multi-level) 페이지 테이블</p>
<p>핵심 아이디어: 대부분의 프로그램은 가상 주소 공간의 아주 일부만 써요. 그러니까 쓰는 부분의 테이블만 만들어요.</p>
<p>32비트, 4KB 페이지, PTE 4B인 x86의 고전적인 방식이에요.</p>
<pre><code>32비트 가상 주소:  [ 10비트 | 10비트 | 12비트 ]
                      │        │        └ 오프셋 (4KB 페이지 안 위치)
                      │        └ 2단계: 페이지 테이블 번호
                      └ 1단계: 페이지 디렉토리 번호

페이지 디렉토리 (1024칸)
 ┌─────┐
 │  0  │──▶ 페이지 테이블 A (1024칸) ──▶ 실제 페이지
 │  1  │    (없음: 이 4MB 영역은 안 씀 → 테이블 안 만듦!)
 │ ... │
 │1023 │──▶ 페이지 테이블 B (1024칸) ──▶ 실제 페이지 (스택)
 └─────┘</code></pre><p>단계 테이블 한 칸이 4MB 영역을 담당해요 (1024개 페이지 × 4KB).
그 영역을 안 쓰면 2단계 테이블을 아예 안 만들어요.</p>
<p>절약 효과: 코드(아래쪽)와 스택(위쪽)만 쓰는 작은 프로그램이라면</p>
<p>1단계 방식: 2^20 × 4B = 4 MB
2단계 방식: 디렉토리 1개(4KB) + 테이블 2개(8KB) = 12 KB</p>
<p>단점: 주소 하나를 변환하려면 테이블을 두 번 읽어야 해요. 그래서 다음 내용으로 TLB(변환 결과를 저장하는 캐시)가 나올 거예요.</p>
<p>Only page directory &amp; active page tables need to be in main memory</p>
<ul>
<li>Page tables can be one page in size – uniform paging mechanism for virtual memory management</li>
</ul>
<p>Can scale up with 3-level scheme and keep page directory size small</p>
<p>Example -- Alpha: say an 8 KB page holds 1K directory entries</p>
<ul>
<li>1 level: 1K page table = 8 MB</li>
<li>2 levels: 1K page directory * 1K page tables = 8 GB</li>
<li>3 levels: 1K page directory * 1K intermediate directories * 1K page tables= 8 TB = 2^43</li>
</ul>
<hr>
<p>+알파</p>
<h1 id="32비트-머신-vs-64비트-머신">32비트 머신 vs 64비트 머신</h1>
<p>&quot;몇 비트&quot;가 뜻하는 것</p>
<p>CPU가 한 번에 다루는 데이터 크기예요. 주로 범용 레지스터의 폭을 말해요. 그리고 포인터(메모리 주소)의 크기도 정해요. 이게 가상 메모리와 직접 연결돼요.</p>
<p>주소가 n비트면, 가리킬 수 있는 주소는 2^n개</p>
<h3 id="32-비트-머신">32 비트 머신</h3>
<pre><code>2^32 = 4,294,967,296 바이트 = 4 GB</code></pre><ul>
<li>프로그램 하나가 볼 수 있는 가상 주소 공간이 최대 4GB예요.</li>
<li>이 4GB도 OS 커널과 나눠 써요. 예를 들어 Linux는 사용자 3GB와 커널 1GB, Windows는 기본으로 2GB씩 나눠요. 그래서 프로그램이 실제로 쓸 수 있는 건 2~3GB예요.</li>
<li>물리 메모리를 4GB보다 많이 꽂아도 기본적으로는 못 써요. PAE라는 기술로 물리 메모리를 늘릴 수는 있지만, 프로그램 하나당 4GB 한계는 그대로예요.</li>
<li>포인터는 4바이트예요.</li>
<li>페이지 테이블은 위에서 본 2단계 (10 | 10 | 12) 로 충분해요.</li>
</ul>
<h3 id="64비트-머신">64비트 머신</h3>
<pre><code>2^64 = 16 EB (엑사바이트) = 약 160억 GB</code></pre><ul>
<li>이론상 엄청나게 크지만, 실제로는 64비트를 다 쓰지 않아요.<ul>
<li>x86-64 (Intel, AMD)와 ARM64 (Apple Silicon 포함)는 보통 48비트 → 2^48 = 256 TB</li>
<li>최신 서버 CPU는 57비트까지 지원해요 (128 PB).</li>
<li>글에 나온 Alpha 21164도 64비트 CPU인데 43비트만 썼어요 (8 TB).</li>
</ul>
</li>
<li>왜 다 안 쓸까? 페이지 테이블 때문이에요. 비트가 많을수록 테이블 단계가 늘어나서 변환이 느려져요. 그리고 256TB면 아직 충분해요.</li>
<li>포인터는 8바이트예요.</li>
</ul>
<p>64비트에서는 페이지 테이블이 더 큰 문제예요</p>
<p>48비트, 4KB 페이지, PTE 8B로 1단계 테이블을 만들면:</p>
<pre><code>VPN = 48 − 12 = 36비트
테이블 크기 = 2^36 × 8B = 512 GB  ← 프로그램 하나당!</code></pre><p>그래서 4단계 테이블을 써요.</p>
<pre><code>48비트 가상 주소:  [ 9 | 9 | 9 | 9 | 12 ]
                    1단계 2단계 3단계 4단계  오프셋</code></pre><p>각 단계 테이블 = 2^9 = 512칸 × 8B = 4KB = 딱 페이지 1개 크기</p>
<p>재밌는 공통점: 단계 수를 정하는 규칙이 같아요. 테이블 하나가 딱 페이지 하나 크기가 되도록 나눠요.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/00cb9d9e-ae36-4fe7-9883-2c0e2548ec7a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/da28b1b6-6f96-4e69-821f-dedb9ac73739/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Prefetching Exercise (Very Important)]]></title>
            <link>https://velog.io/@houston_guy2/Software-Prefetching-Very-Important</link>
            <guid>https://velog.io/@houston_guy2/Software-Prefetching-Very-Important</guid>
            <pubDate>Mon, 26 Oct 2026 16:07:28 GMT</pubDate>
            <description><![CDATA[<h1 id="exercises-on-prefetching">Exercises on prefetching</h1>
<p>Will be on Exam</p>
<h1 id="ex-1">Ex 1</h1>
<p>For the code below, (1) determine which accesses are likely to cause data cache misses.
Assume we have an 8KB direct-mapped data cache with 16-byte blocks. The elements of a and b are 8 bytes long. There are 3 rows and 100 columns for a and 101 rows and 3 columns for b.
They are not in the cache at the start of the program. Ignore the potential capacity and conflict misses. The array is stored in row order. </p>
<pre><code class="language-c">for(i=0; i&lt;3; i++)
    for(j=0; j&lt;100; j++)
        a[i][j] = b[j][0] * b[j+1][0];</code></pre>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/983b7ebf-65b0-4373-8432-fa95301dee61/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/41183822-cfa1-4037-9fd1-426030997bad/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/c9e6792f-e9e3-4b7e-b527-c468bde63a12/image.png" alt=""></p>
<hr>
<h1 id="ex-2">Ex 2</h1>
<p>Applying the software prefetching technique to reduce cache misses, assume the prefetching has no benefit until the eighth iteration. Calculate the number of prefetch instructions executed and the misses avoided by prefetching.</p>
<pre><code class="language-c">for(j=0; j&lt;100; j++) {
    prefetch(b[j+7][0]);
    prefetch(a[0][j+7]);
    a[0][j]= b[j][0] * b [j+1][0]; }

for(i=1; i&lt;3; i++)
    for(j=0; j&lt;100; j++) {
        prefetch(a[i][j+7]);
        a[i][j] = b[j][0] * b[j+1][0];}</code></pre>
<h4 id="1-문제에서-주는-가정">1. 문제에서 주는 가정</h4>
<ul>
<li>프리페치는 7칸 앞(j+7)의 데이터를 요청해요.</li>
<li>“eighth iteration까지는 효과가 없다”는 말은, 1번째 반복(j=0)에 요청한 데이터가 8번째 반복(j=7)부터 쓸모 있다는 뜻이에요. 데이터가 도착하는 데 시간이 걸리기 때문이에요.</li>
<li>그래서 처음 7번의 반복(j = 0~6)에서 쓰는 데이터는 미리 못 가져와요. 이 데이터는 프리페치 대상(j+7 = 7부터 시작)에 한 번도 들어가지 않아서, 원래대로 미스가 나요.</li>
</ul>
<h4 id="2-루프를-둘로-나눈-이유">2. 루프를 둘로 나눈 이유</h4>
<p>Ex 1에서 b는 i = 0일 때 전부 캐시에 들어오고, i = 1, 2에서는 모두 히트였어요.</p>
<ul>
<li>첫 번째 루프 (i = 0): b와 a 둘 다 처음 보는 데이터예요. 그래서 둘 다 프리페치해요.</li>
<li>두 번째 루프 (i = 1, 2): b는 이미 캐시에 있어요. b를 또 프리페치하면 명령어 비용만 들어서, a만 프리페치해요.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/bcd01e7c-2821-4aa9-8d7e-5a5d5a0b4a55/image.png" alt=""></p>
<h4 id="3-프리페치-명령어-수">3. 프리페치 명령어 수</h4>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/dbc739dd-f67d-4130-b8e3-ea7b2311fa08/image.png" alt="">
조건문이 없어서 블록이 이미 캐시에 있어도 매 반복 실행돼요. 그래서 반복 횟수 × 명령어 개수로 그대로 세면 돼요.</p>
<h4 id="4-남는-미스-계산">4. 남는 미스 계산</h4>
<p>첫 번째 루프 (i = 0)</p>
<ul>
<li>b: b[0][0] ~ b[6][0], 원소 7개는 프리페치되지 않아요. b[j][0]마다 블록이 다르니까(Ex 1에서 본 24바이트 간격) 미스 7번이에요.</li>
<li>a: a[0][0] ~ a[0][6], 원소 7개가 프리페치되지 않아요. 블록 하나에 2개씩 들어가니까 블록 수로 세요.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/e892c2cf-194b-47f2-91d7-7ffcd0f30f9d/image.png" alt=""></p>
<hr>
<h1 id="ex-3">Ex 3</h1>
<p>Calculate the time saved. Ignore I-cache misses and assume there are no conflict or capacity misses in D-cache. Assume that prefetches can overlap with each other and with cache misses.
Here are the key loop times ignoring cache misses: the original loop takes 7 cycles per iteration, the first prefetch loop takes 9 cycles per iteration, and the second prefetch loop takes 8 cycles per iteration (including the overhead of the outer for loop). A miss takes 100 cycles. </p>
<p>계산 방법</p>
<p>전체 시간 = (반복 횟수 × 반복당 사이클) + (미스 횟수 × 100)</p>
<p>반복당 사이클은 미스가 없을 때의 시간이에요. 미스가 나면 100사이클씩 더해요.</p>
<p>가정이 뜻하는 것</p>
<ul>
<li>I-cache 미스 무시, 용량·충돌 미스 없음: Ex 1, Ex 2에서 센 미스(251, 19)를 그대로 쓰면 돼요.</li>
<li>프리페치끼리, 그리고 미스와도 겹칠 수 있음: 프리페치가 메모리에서 데이터를 가져오는 시간은 따로 더하지 않아요. 프리페치 명령어를 실행하는 비용은 이미 반복당 사이클(9, 8)에 들어 있어요. 원래 루프가 7사이클인데 9, 8사이클로 늘어난 게 바로 그 비용이에요.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/fff17f22-f272-4b0e-b7ee-e4d196803a6b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/22f46187-fbd5-46db-b318-741fa8d0f388/image.png" alt=""></p>
<p>프리페치 명령어 때문에 400사이클이 늘었지만, 미스가 줄어서 23,200사이클이 줄었어요. 앞에서 본 &quot;비용 &lt; 이득&quot; 조건이 실제 숫자로 나온 거예요. 미스 한 번(100사이클)이 프리페치 명령어 하나(약 1사이클)보다 훨씬 비싸서 이득이 커요.</p>
<p>시험 팁: “time saved”를 물으면 22,800 사이클, “speedup”을 물으면 6.2배로 답하면 돼요. 미스 19번을 루프별로 11번과 8번으로 나누는 것이 가장 실수하기 쉬운 부분이에요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hardware, Software Prefetching]]></title>
            <link>https://velog.io/@houston_guy2/Hardware-Prefetching</link>
            <guid>https://velog.io/@houston_guy2/Hardware-Prefetching</guid>
            <pubDate>Mon, 26 Oct 2026 15:55:20 GMT</pubDate>
            <description><![CDATA[<h1 id="0-hardware-prefetching">0. Hardware Prefetching</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/794a81a9-d1d8-490f-8ca2-d5db70693185/image.png" alt=""></p>
<p>No need for programmer or compiler intervention
No changes to existing executables
Take advantage of run-time information</p>
<p>앞 글의 프리페칭은 프로그래머나 컴파일러가 &quot;이거 미리 가져와&quot; 하고 코드에 넣는 방식이었어요. 하드웨어 프리페칭은 CPU가 실행 중에 메모리 접근 패턴을 지켜보다가 알아서 미리 가져오는 방식이에요.</p>
<p>프로그램을 고칠 필요가 없어요. 이미 만들어진 실행 파일도 그대로 빨라져요.
실행 중에 실제로 일어나는 일을 보고 예측해요.</p>
<h3 id="major-design-questions">Major design questions</h3>
<ul>
<li>What to prefetch?
  » Next sequential cache line(s), strided patterns, …</li>
<li>When to prefetch?
  » On an access (prefetch always), on a miss (prefetch on miss), when other prefetched data used (tagged prefetch)</li>
<li>Where to place prefetched data
  » In the cache or in a special prefetch buffer</li>
<li>How to handle VM exceptions?
  » Don’t prefetch beyond a page</li>
</ul>
<p>Tagged prefetch: 미리 가져온 블록에 표시(tag)를 해 둬요. 그 블록이 처음 쓰이는 순간 &quot;이 예측이 맞았네&quot; 하고 다음 블록을 가져와요. 실제로 쓰이는 속도에 맞춰 한 발씩 앞서가는 방식이에요.</p>
<p>페이지를 넘지 않는 이유: 가상 메모리에서 바로 다음 페이지가 실제 메모리에서도 옆에 있다는 보장이 없어요. 아예 할당이 안 돼 있을 수도 있어요. 함부로 넘어가면 엉뚱한 데이터를 가져오거나 페이지 폴트를 일으킬 수 있어요.</p>
<hr>
<h1 id="1-simple-sequential-prefetching">1. Simple Sequential Prefetching</h1>
<p>미스가 나면 그 다음 블록 하나도 같이 가져와요.</p>
<ul>
<li>On a cache miss, fetch one sequential memory block<pre><code>  - Exploits spatial locality in both instructions &amp; data</code></pre></li>
<li>Mimic behavior of larger cache line size without really do it</li>
<li>Extend to fetching N sequential memory blocks</li>
<li>Fixed vs. dynamic number of blocks for prefetching</li>
</ul>
<pre><code>블록 A에서 미스 → A를 가져오면서 A+1도 미리 가져옴
→ 프로그램이 A+1을 쓸 때 이미 와 있음 (미스 없음)</code></pre><p>효과는 블록 크기를 키운 것과 비슷해요. 하지만 실제 블록 크기는 그대로라서, 큰 블록의 단점(블록 개수가 줄어드는 것)은 없어요.</p>
<p>인텔은 이걸 “Adjacent Cache Line Prefetch”라고 불러요.
한 번에 N개를 가져오도록 늘릴 수도 있어요. N은 고정일 수도 있고, 상황에 따라 바뀔 수도 있어요.</p>
<p>Pros:</p>
<ul>
<li>No changes to executables</li>
<li>Simple hardware</li>
</ul>
<p>Cons:</p>
<ul>
<li>Only applies for good spatial locality</li>
<li>Works poorly for nonsequential accesses</li>
<li>Unnecessary prefetches for array accesses with large stride</li>
</ul>
<hr>
<h1 id="2-stream-prefetching">2. Stream Prefetching</h1>
<p>문제: 미스가 났을 때 N개를 한꺼번에 가져오면, N개를 다 쓰고 나서 또 미스가 나요. N블록마다 한 번씩 멈추는 셈이에요.</p>
<p>해결: 미리 가져온 블록이 하나 쓰일 때마다 하나를 더 가져와서, 항상 N개 앞서 있게 해요. 이걸 위한 별도 공간이 스트림 버퍼예요.</p>
<p>Sequential prefetching problem</p>
<ul>
<li>Performance slows down once every N cache lines</li>
</ul>
<p>Stream prefetching is a continuous version of prefetching</p>
<ul>
<li>Stream buffer can fit N cache lines</li>
<li>On a miss, start fetching N sequential cache lines</li>
<li>On a stream buffer hit: Move cache line to cache, start fetching line (N+1) In other words, stream buffer tries to stay N cache lines ahead</li>
</ul>
<p>Design issues</p>
<ul>
<li>When is a stream buffer allocated</li>
<li>When is a stream buffer released
<img src="https://velog.velcdn.com/images/houston_guy2/post/bf43b3f6-9d8e-4e0c-9410-4c641fe56a61/image.png" alt=""></li>
</ul>
<p>동작 예시 (N = 4)</p>
<pre><code>① A에서 캐시 미스
   → A는 캐시로, A+1 ~ A+4는 스트림 버퍼로 가져오기 시작
   스트림 버퍼: [A+1][A+2][A+3][A+4]   (도착 전엔 &quot;아직 안 옴&quot; 표시)

② 프로그램이 A+1 요청 → 캐시엔 없지만 스트림 버퍼에 있음!
   → 1사이클 만에 캐시로 옮김, 나머지 한 칸씩 앞으로, 빈자리에 A+5 가져오기
   스트림 버퍼: [A+2][A+3][A+4][A+5]

③ A+2 요청 → 또 버퍼에서 → [A+3][A+4][A+5][A+6]  ... 계속 4개 앞서감

④ 갑자기 전혀 다른 주소 Z에서 미스 (순서가 끊김)
   → 버퍼를 비우고 Z부터 다시 시작: [Z+1][Z+2][Z+3][Z+4]</code></pre><p>왜 캐시가 아니라 별도 버퍼에 넣을까? 예측이 틀리면 쓸데없는 데이터가 캐시의 좋은 데이터를 밀어내요(캐시 오염). 버퍼에 두면 실제로 쓰일 때만 캐시로 들어가니 안전해요.
각 칸에는 &quot;데이터 도착함/아직&quot; 표시(available bit)가 있어요. 요청한 블록이 아직 오는 중이면 기다려요.</p>
<p>비유: 회전초밥 레일에 다음 접시 4개를 항상 올려 두는 것. 하나 집으면 하나를 새로 올려요. 손님이 갑자기 다른 메뉴를 시키면 레일을 비우고 새 메뉴로 채워요.</p>
<hr>
<h1 id="3-prefetching-with-arbitrary-strides">3. Prefetching with Arbitrary Strides</h1>
<p>마지막 단점을 해결하는 방법이에요. 루프에서는 같은 명령어가 매번 일정한 간격으로 떨어진 주소를 읽는 경우가 많아요.</p>
<pre><code>같은 명령어가 읽은 주소: 1000 → 1400 → 1800 → 2200 ...
                         (+400)  (+400)  (+400)   ← 간격 D = 400</code></pre><ul>
<li>Employ special logic to monitor the processor’s address referencing pattern</li>
<li>Detect constant stride array references originating from looping structures</li>
<li>Compare successive addresses used by load or store instructions</li>
</ul>
<h2 id="동작-원리">동작 원리</h2>
<p>명령어 mi가 반복할 때마다 주소 a1, a2, a3…를 읽는다고 해요.</p>
<ol>
<li>a1, a2를 보고 간격 D = a2 − a1을 계산해요. D가 0이 아니면 프리페치를 시작해요.</li>
<li>다음 주소를 a3 = a2 + D로 예측하고 미리 가져와요.</li>
<li>예측이 계속 맞으면 a4 = a3 + D, … 이렇게 이어가요. 예측이 틀리면 멈춰요.</li>
</ol>
<p>순차 프리페칭과의 차이: 순차는 &quot;바로 옆&quot;만 가져오지만, stride는 얼마나 건너뛰는지 학습해서 정확히 그 위치를 가져와요.</p>
<hr>
<h1 id="4-reference-prediction-table-rpt">4. Reference Prediction Table (RPT)</h1>
<p>stride 프리페칭을 실제로 구현하는 표예요. 명령어마다 한 줄씩, 그 명령어의 접근 패턴을 기록해요.</p>
<p>Hold information for the most recently used memory instructions to predict their access pattern</p>
<ul>
<li>Address of the memory instruction</li>
<li>Previous address accessed by the instruction</li>
<li>Stride value</li>
<li>State field</li>
</ul>
<h2 id="organization-of-rpt">Organization of RPT</h2>
<p><span style="color:red">it&#39;s not in the Final test
but it&#39;s used alot in industry</span></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/b2900287-a68d-4b86-a548-4bb8a98928bc/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/1584231c-5107-4d18-9ed6-c0465bb7882c/image.png" alt=""></p>
<h3 id="example">Example</h3>
<pre><code class="language-c">float a[100][100], b[100][100], c[100][100];
    for (i = 0; i &lt; 100; i++)
        for (j = 0; j &lt; 100; j++)
            for (k = 0; k &lt; 100; k++)
                a[i][j] += b[i][k] * c[k][j]</code></pre>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/bc203034-98fc-44db-b9b6-a1adff1a1a65/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/4756cf71-9f41-44e2-9327-934b62925bac/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/d0504072-9e35-4f64-89c4-45c256360fb5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/1d6823a8-ab56-42a7-8317-5428473d8767/image.png" alt=""></p>
<p>예를 들어 b가 주소 50000, c가 90000, a가 10000에서 시작한다고 하면 RPT는 이렇게 변해요.</p>
<pre><code>1회차: b: 이전=50000  stride=0    initial     (처음 봄)
       c: 이전=90000  stride=0    initial
       a: 이전=10000  stride=0    initial

2회차: b: 이전=50004  stride=4    transient   (간격 4 발견, 확인 중)
       c: 이전=90400  stride=400  transient   (간격 400 발견)
       a: 이전=10000  stride=0    steady      (같은 주소 → 간격 0이 맞음)

3회차: b: 이전=50008  stride=4    steady  → 50012 미리 가져옴
       c: 이전=90800  stride=400  steady  → 91200 미리 가져옴</code></pre><p>핵심은 명령어(PC)별로 따로 기록한다는 점이에요. 그래서 b, c처럼 간격이 서로 다른 접근이 섞여 있어도 각각 정확히 예측할 수 있어요.</p>
<hr>
<h1 id="software-prefetching">Software Prefetching</h1>
<pre><code class="language-c">for (i=0; i&lt;N; i++) {
    prefetch(a[i+8]); // 8번 뒤에 쓸 a를 지금 미리 요청
    prefetch(b[i+8]); // b도 마찬가지
    sum += a[i]*b[i]; // 지금 계산은 이미 와 있는 데이터로
}

Doesn’t have to be correct!
prefetch(N+8);</code></pre>
<p>프로그래머나 컴파일러가 “8번 뒤에 쓸 데이터를 지금 가져와 둬” 라고 코드에 직접 넣는 방식이에요. 앞 글의 하드웨어 프리페칭과 반대예요.</p>
<p>왜 8번 뒤일까? 메모리에서 데이터가 오는 데 시간이 걸리니까, 그 시간 동안 루프가 몇 번 돌지 계산해서 정해요. 지금 요청하면 8번 돌고 나서 도착하는 셈이라, 그때 쓸 데이터를 지금 요청하는 거예요.</p>
<p>“Doesn’t have to be correct!”의 의미
루프 끝부분에서는 a[N+7]처럼 배열 범위를 벗어난 주소를 프리페치해요. 그래도 괜찮아요. 프리페치는 &quot;가능하면 가져와 줘&quot;라는 힌트일 뿐이라서, 틀려도 결과는 바뀌지 않고 대역폭만 조금 낭비돼요. 일반 load였다면 프로그램이 죽을 수도 있는 주소예요.</p>
<h2 id="data-prefetch">Data Prefetch</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/ff3c7eb1-2114-444e-ad3c-f4d68e9514a5/image.png" alt=""></p>
<ul>
<li>Register Prefetch: load data int register</li>
<li>Cache Prefetch: load into cache</li>
<li>Special prefetching instructions cannot cause faults. The addr does not cause an exception (e.g. page fault)</li>
<li>Requires nonblocking cache</li>
</ul>
<p>Issuing Prefetch Instructions takes time</p>
<ul>
<li>Is cost of prefetch issues &lt; savings in reduced misses?</li>
</ul>
<p>프리페치 명령어가 갖춰야 할 조건</p>
<ul>
<li>예외(fault)를 일으키면 안 돼요. 잘못된 주소나 아직 메모리에 없는 페이지를 가리켜도 페이지 폴트 없이 그냥 무시돼야 해요. 그래서 &quot;틀려도 된다&quot;가 가능한 거예요.</li>
<li>논블로킹 캐시(nonblocking cache)가 필요해요. 프리페치한 데이터가 오는 동안에도 캐시가 다른 load 요청을 계속 처리할 수 있어야 해요. 캐시가 프리페치를 기다리느라 멈춰 버리면, 미리 가져오는 의미가 없어요.</li>
</ul>
<p>비용 문제</p>
<p>프리페치도 명령어라서 실행하는 데 시간이 걸려요. 그래서 따져봐야 해요.</p>
<blockquote>
<p>프리페치 명령어 실행 비용 &lt; 줄어든 미스로 아끼는 시간 이어야 이득</p>
</blockquote>
<p>예를 들어 이미 캐시에 있는 데이터를 프리페치하면 시간만 날려요. 아래 Ex 1에서 이게 중요해져요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[memory basic]]></title>
            <link>https://velog.io/@houston_guy2/memory-basic</link>
            <guid>https://velog.io/@houston_guy2/memory-basic</guid>
            <pubDate>Thu, 08 Oct 2026 02:59:29 GMT</pubDate>
            <description><![CDATA[<h2 id="step-1-memory-is-a-long-row-of-numbered-boxes">Step 1: Memory is a long row of numbered boxes</h2>
<p>Your computer&#39;s memory (RAM) is like a huge row of tiny boxes:</p>
<pre><code> box #:   1000  1001  1002  1003  1004  1005  1006  1007  ...
        ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
        │     │     │    │     │    │     │    │     │ ...
        └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘</code></pre><ul>
<li>Each box holds 1 byte, a very small piece of data.</li>
<li>Each box has a number called its address, like a house number on a street.</li>
</ul>
<p>Everything in your program (numbers, arrays, text) is stored in these boxes.</p>
<h2 id="step-2-variables-and-types">Step 2: Variables and types</h2>
<p>When you write</p>
<pre><code class="language-c">int a = 12;</code></pre>
<p>you are telling the computer: &quot;Reserve some boxes, call them a, and put the number 12 in them.&quot;</p>
<p>The type (int here) answers two questions:</p>
<ol>
<li>How many boxes (bytes) to reserve</li>
<li>What kind of value is stored there</li>
</ol>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/d55fb93d-463a-4087-a5f7-53b482be9259/image.png" alt=""></p>
<p>Use double when you need more accuracy, as in scientific computing. The cost is that it uses 8 bytes instead of 4.</p>
<pre><code>int a = 12;     →  [ 4 boxes ]
double d = 3.14 →  [ 8 boxes ]</code></pre><h2 id="step-3-sizeof-which-asks-how-many-bytes-something-takes">Step 3: sizeof, which asks how many bytes something takes</h2>
<p>sizeof tells you the size of a type or variable in bytes:</p>
<pre><code class="language-c">sizeof(int)     // 4
sizeof(float)   // 4
sizeof(double)  // 8</code></pre>
<p>Why not just type the number 8 yourself? Because sizes can differ between computers, and sizeof(double) is clearer than a bare 8. It says what you mean, and it is always correct for the machine you&#39;re on.</p>
<h2 id="step-4-arrays-are-many-boxes-in-a-row">Step 4: Arrays are many boxes in a row</h2>
<pre><code class="language-c">float x[9];</code></pre>
<p>This reserves 9 floats side by side:</p>
<pre><code> index:    0    1    2    3    4    5    6    7    8
        ┌────┬────┬────┬────┬────┬────┬────┬────┬────┐
 x:     │    │    │   │    │   │    │    │    │   │
        └────┴────┴────┴────┴────┴────┴────┴────┴────┘
          4B   4B   4B   4B   4B   4B   4B   4B   4B    
        → total 9 × 4 = 36 bytes</code></pre><h2 id="step-5-pointers-are-variables-that-hold-an-address">Step 5: Pointers are variables that hold an address</h2>
<p>A pointer doesn&#39;t hold a normal value. It holds the address of another piece of memory.</p>
<p>Think of a sticky note with a locker number written on it. The note isn&#39;t the locker; it just tells you where the locker is.</p>
<pre><code class="language-c">double* p;   // p is a pointer: it can hold the address of a double</code></pre>
<p>The * after the type means &quot;pointer to&quot;. So double* means &quot;address of a double&quot;.</p>
<p>On a 64-bit computer, every pointer is 8 bytes, no matter what it points to, because an address is always the same size.</p>
<pre><code>      p (8 bytes)                       somewhere else in memory
  ┌──────────────┐                     ┌────────────────────┐
  │  addr: 5000 │ ──── points to ───→ │   3.14  (double)  │ ← at box #5000
  └──────────────┘                     └────────────────────┘</code></pre><h2 id="step-6-stack-and-heap-the-two-places-memory-comes-from">Step 6: Stack and heap, the two places memory comes from</h2>
<p>Memory for your program comes from two main areas:</p>
<h3 id="stack-your-functions-desk">Stack: your function&#39;s desk</h3>
<ul>
<li>Ordinary variables declared inside a function go here: int a;, float x[9];</li>
<li>Automatic. When the function ends, everything on its stack is cleaned up.</li>
<li>The size must be known when you write the code (e.g. exactly 9 floats).</li>
<li>Small (typically 1–8 MB total).</li>
</ul>
<h3 id="heap-a-big-warehouse">Heap: a big warehouse</h3>
<ul>
<li>You ask for space while the program is running, using malloc.</li>
<li>Stays until you return it with free. Nothing is cleaned up automatically.</li>
<li>Can be very large, and the size can be decided at runtime.</li>
<li>The warehouse gives you an address to the space, which you store in a pointer.</li>
</ul>
<h2 id="step-7-malloc-which-asks-the-heap-for-memory">Step 7: malloc, which asks the heap for memory</h2>
<p>malloc stands for memory allocate. You tell it how many bytes you want, and it:</p>
<ol>
<li>Reserves that many bytes on the heap</li>
<li>Gives you back the address of the first byte</li>
</ol>
<p>Here is the line from your homework, taken apart piece by piece:</p>
<pre><code class="language-c">double* y = (double*) malloc(sizeof(double) * 10);</code></pre>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f7b2dba3-3e1b-44b1-a070-6f9c16aee918/image.png" alt=""></p>
<p>The result looks like this:</p>
<pre><code>   STACK                               HEAP
  ┌───────────────┐                   ┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
  │ y (8 bytes)  │ ───────────────→   │ d0 │ d1 │ d2│ d3 │ d4 │ d5│ d6 │ d7 │ d8│ d9 │
  │ holds address│                   └────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
  └───────────────┘                     10 doubles × 8 bytes = 80 bytes</code></pre><p>When you&#39;re done, give the memory back:</p>
<pre><code class="language-c">free(y);</code></pre>
<hr>
<p>왜 x 칸이 2000개야? 앞에 코드는 n이 2000 이라 했고, malloc으로 heap이 16000인거잖아</p>
<p>맞아요, malloc이 heap에서 받아온 건 16,000 바이트예요. 그런데 x가 double*라서 그 공간을 8바이트씩 묶어서 한 칸으로 써요.</p>
<pre><code>16,000 바이트 ÷ 8 바이트(double 한 개) = 2000 칸</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Reducing Missed by Compiler Optimizations- (Final range start)]]></title>
            <link>https://velog.io/@houston_guy2/Reducing-Missed-by-Compiler-Optimizations</link>
            <guid>https://velog.io/@houston_guy2/Reducing-Missed-by-Compiler-Optimizations</guid>
            <pubDate>Wed, 07 Oct 2026 16:14:26 GMT</pubDate>
            <description><![CDATA[<h4 id="the-optimized-software-can-greatly-reduce-the-miss-rates">The optimized software can greatly reduce the miss rates</h4>
<h3 id="instructions">Instructions</h3>
<ul>
<li>Reorder procedures in memory so as to reduce conflict misses, without affecting correctness</li>
<li>Profiling to look at conflicts(using tools they developed)</li>
<li>Entry point of basic block at the beginning of a block</li>
<li>Branch straightening</li>
</ul>
<h3 id="data">Data</h3>
<ol>
<li>Merging Arrays: improve spatial locality by single array of compound elements vs. 2 arrays</li>
<li>Loop Interchange: change nesting of loops to access data in order stored in memory</li>
<li>Loop Fusion: Combine 2 independent loops that have same looping and some variables overlap</li>
<li>Blocking: Improve temporal locality by accessing “blocks” of data repeatedly vs. going down whole columns or rows</li>
</ol>
<hr>
<h2 id="1-merging-arrays">1. Merging Arrays</h2>
<pre><code>// Before: 2 sequential arrays //
int val[SIZE];
int key[SIZE];

//After: 1 array of stuctures //
struct merge {
    int val;
    int key;
};

struct merge merged_array[SIZE];</code></pre><p>Reducing conflicts between val &amp; key (they might conflict for a single block in the cache); improve spatial locality when they are accessed in a interleaved fashion</p>
<h3 id="colum-order">Colum order:</h3>
<p>store first colum, then second colum, then third….</p>
<pre><code>Example:
– 6x6 array A
– Each element 1 word
– Block size: 4 word
– For i=1 to 6; X+= a[1,i]; how many blocks are loaded into cache</code></pre><p><span style="color:red">For final
you need to know how many misses in this pic
and prof will give the code</span></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/e40e4c07-43a4-40d1-a8c3-117d0258c6e9/image.png" alt=""></p>
<h3 id="row-order">Row order:</h3>
<p>store first row, then second row, then third….</p>
<pre><code>Example:
– 6x6 array A
– Each element 1 word
– Block size: 4 word
– For i=1 to 6; X+= a[1,i]; how many blocks are loaded into cach</code></pre><p><img src="https://velog.velcdn.com/images/houston_guy2/post/ca917628-5490-4c4c-808f-156920f5f040/image.png" alt=""></p>
<h2 id="2-loop-interchange-example">2. Loop Interchange Example</h2>
<p>Assume the array storage is in row order</p>
<pre><code class="language-c">for (k = 0; k &lt; 100; k = k+1)
    for (j = 0; j &lt; 100; j = j+1)
        for (i = 0; i &lt; 5000; i = i+1)
            x[i][j] = 2 * x[i][j];

/* After */
for (k = 0; k &lt; 100; k = k+1)
    for (i = 0; i &lt; 5000; i = i+1)
        for (j = 0; j &lt; 100; j = j+1)
            x[i][j] = 2 * x[i][j];</code></pre>
<ul>
<li>Sequential accesses instead of striding through memory every 100 words; improved spatial locality</li>
</ul>
<h2 id="3-loop-fusion-example">3. Loop Fusion Example</h2>
<pre><code class="language-c">/* Before */
for (i = 0; i &lt; N; i = i+1)
    for (j = 0; j &lt; N; j = j+1)
        a[i][j] = 1/b[i][j] * c[i][j];

for (i = 0; i &lt; N; i = i+1)
    for (j = 0; j &lt; N; j = j+1)
        d[i][j] = a[i][j] + c[i][j];</code></pre>
<ul>
<li>Many programs have separate loops that operate on the same data</li>
<li>Combining these loops allows a program to take advantage of temporal locality by grouping operations on the same (cached) data together.</li>
</ul>
<pre><code class="language-c"> /* After */
for (i = 0; i &lt; N; i = i+1)
    for (j = 0; j &lt; N; j = j+1)
        { a[i][j] = 1/b[i][j] * c[i][j];
          d[i][j] = a[i][j] + c[i][j];}</code></pre>
<ul>
<li>2 misses per access to a &amp; c vs. one miss per access; improve temporal locality</li>
</ul>
<h2 id="4-blocking">4. Blocking</h2>
<p>행렬 곱셈처럼 큰 데이터를 쓰는 작업에서, 행이나 열을 끝까지 쭉 훑지 않고 캐시에 쏙 들어가는 작은 정사각형 조각(블록) 을 하나 정해요. 그 조각을 여러 번 다 쓴 다음에 다음 조각으로 넘어가요.</p>
<p>비유: 시험공부할 때 책 10권을 동시에 펴서 한 줄씩 왔다 갔다 하지 않고, 한 챕터를 책상에 펴 놓고 끝낸 뒤 다음 챕터로 가는 것.</p>
<hr>
<h1 id="prefetching">Prefetching</h1>
<p>Idea: fetch data into the cache before processors request them</p>
<ul>
<li>Can address cold misses</li>
<li>Can be done by the programmer, compiler, or hardware</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/b0411018-05b0-4f6a-8964-29c40e8b80fe/image.png" alt=""></p>
<p>필요해지기 전에 미리 캐시로 가져오는 것이에요. 프로그래머, 컴파일러, 하드웨어 누구든 할 수 있어요. 처음 쓰는 데이터라서 어차피 나는 미스(cold miss)도 막을 수 있어요.</p>
<h4 id="characteristics-of-ideal-prefetching">Characteristics of ideal prefetching</h4>
<pre><code>You only prefetch data that are truly needed
- Avoid bandwidth waste

You issue prefetch requests early enough
- To hide the memory latency

You don’t issue prefetch requests too early
- To avoid cache pollution</code></pre><p>Prefetching relies on having extra memory bandwidth that can be used without penalty</p>
<h4 id="prefetching-terminology">Prefetching Terminology</h4>
<ul>
<li>Prefetch Hit: Prefetched line that was hit in the cache before being replaced (miss avoided)</li>
<li>Prefetch Miss: Prefetched line that was replaced before being accessed</li>
<li>Prefetch Rate: Prefetches per instruction (or 1000inst.)
Accuracy: Percentage of prefetch hits to all prefetches</li>
<li>Coverage: Percentage of misses avoided due to prefetching
100 x (Prefetch Hits / (Prefetch Hits + Cache
Misses))</li>
</ul>
<h2 id="classification-of-prefetched-lines">Classification of Prefetched Lines</h2>
<p>Useful Prefetch</p>
<ul>
<li>Prefetch hit before being replaced</li>
<li>Results in avoiding a cache miss</li>
</ul>
<p>Useless Prefetch</p>
<ul>
<li>Prefetch is replaced before being accessed (prefetch miss)</li>
<li>Downside: Increases demand for cache bandwidth, power</li>
</ul>
<p>Harmful Prefetch</p>
<ul>
<li>Prefetch is replaced before being accessed AND</li>
<li>Prefetch replaces a line that is requested later (cache pollution)</li>
<li>Results in an additional cache miss</li>
<li>power</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Reducing Miss Penalty - mid term]]></title>
            <link>https://velog.io/@houston_guy2/Reducing-Miss-Penalty-mid-term</link>
            <guid>https://velog.io/@houston_guy2/Reducing-Miss-Penalty-mid-term</guid>
            <pubDate>Mon, 05 Oct 2026 16:13:39 GMT</pubDate>
            <description><![CDATA[<h1 id="1-early-restart-critical-word-first">1. Early restart, Critical Word First</h1>
<ul>
<li>CPU normally needs just one word of the block at a time. Don’t wait for full block before restarting CPU</li>
<li>Early restart — As soon as the requested word of the block arrives, send it to the CPU and let the CPU continue execution</li>
<li>Critical Word First —Request the missed word first from memory and send it to the CPU as soon as it arrives; let the CPU continue execution while filling the rest of the words in the block</li>
<li>Long blocks more popular today -&gt; Critical Word 1st Widely used</li>
</ul>
<hr>
<h1 id="1-6-is-not-on-exam">1-6 is not on EXAM</h1>
<hr>
<p>from now on it&#39;s in exame</p>
<h1 id="span-stylecolororange-2-merging-write-buffersspan"><span style="color:Orange"> 2. Merging Write Buffers</span></h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/a626df40-471b-4c2a-9746-18a7068085f3/image.png" alt=""></p>
<p>A write buffer removes most of the time the CPU would otherwise spend waiting on writes. A merging write buffer (also called write merging) makes it more efficient still. The exercise at the end builds on both.</p>
<h2 id="a-why-a-write-buffer">A. Why a write buffer?</h2>
<p>With a write-through L1, every store must also be written to the next level (L2 or memory), which is slow. Without help, the CPU would wait on every store.</p>
<p>A write buffer is a small queue between L1 and the next level:</p>
<ol>
<li>On a store, the address and data are placed in the write buffer.</li>
<li>From the CPU&#39;s point of view, the write is finished, so it continues right away.</li>
<li>The buffer sends its entries to the next level in the background, oldest first.</li>
</ol>
<p>The CPU waits only when the buffer is full.</p>
<h2 id="b-the-three-cases-on-your-slide">B. The three cases on your slide</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/11b88c96-1e32-4a4c-8d04-f4e8ccd72961/image.png" alt=""></p>
<h2 id="c-write-merging-how-it-works">C. Write merging: how it works</h2>
<h4 id="span-stylecolorlightgreenwrite-merging-offspan"><span style="color:lightgreen">Write merging OFF</span></h4>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/90d1f122-082e-456d-9472-a7bff30bad2d/image.png" alt=""></p>
<hr>
<h4 id="span-stylecolorlightgreenwrite-merging-onspan"><span style="color:lightgreen">Write merging ON</span></h4>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/903a66bc-a4a6-4f23-9c30-1637ca3b5154/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/abb98759-1122-4dd1-8486-d53a267d280d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/a65fb69c-0b84-431e-85a2-a6ffe18f1e1a/image.png" alt=""></p>
<p>Each buffer entry holds a whole block&#39;s worth of space, for example 4 words of 8 bytes, with a valid bit per word. When a new store arrives, the buffer checks: does a valid entry already cover this address?</p>
<ul>
<li>Yes: put the word into that entry and set its valid bit (merge).</li>
<li>No: use a new entry.</li>
</ul>
<p>Without merging, each store takes its own entry even when the stores are to neighboring addresses, so the buffer fills up quickly.</p>
<p>In the simulator below, the stores come faster than the buffer can drain. Run the same 9 stores with merging off, then on, and compare the stalls.</p>
<h2 id="d-why-write-merging-helps-common-exam-question">D. Why write merging helps (common exam question)</h2>
<ol>
<li>Fewer stalls. Neighboring stores share one entry, so the buffer fills up more slowly.</li>
<li>Fewer, more efficient writes to the next level. Sending one entry with 4 words uses the memory bus better than 4 separate one-word writes, since each transfer pays the setup cost only once.</li>
<li>Better use of buffer space. Without merging, each entry holds one word and the rest of its space is wasted.</li>
</ol>
<p>Two more points worth knowing:</p>
<ul>
<li>Reads must check the write buffer. A load may want data that is still sitting in the buffer and not yet in L2. The cache either checks the buffer for a matching address or waits for the buffer to drain, so the load doesn&#39;t read a stale value.</li>
<li>I/O addresses are not merged. Writes to device registers must each happen separately and in order.</li>
</ul>
<h2 id="exercise-write-time-with-a-write-through-l1">Exercise: write time with a write-through L1</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/bb440b86-9158-4b35-8e20-dfa7816c7e10/image.png" alt=""></p>
<blockquote>
<p>L1 is write-through. 95% of the time the store can go into the write buffer; 5% of the time the buffer is full. How do you compute the write time?</p>
</blockquote>
<h3 id="step-1-identify-the-two-cases">Step 1: Identify the two cases</h3>
<p>Case A, buffer has space (95%): the store goes into L1 and the buffer, and the CPU continues. The time is just the L1 write time, the L1 hit time.</p>
<p>Case B, buffer is full (5%): the CPU must wait until an entry is written to L2 and freed. That wait is the time to write one entry into L2, including L2 misses:</p>
<pre><code>T_L2write = hit time L2 + miss rate L2 × miss penalty L2</code></pre><h3 id="step-2-combine-with-the-probabilities">Step 2: Combine with the probabilities</h3>
<pre><code>Write time = 0.95 × T_L1  +  0.05 × (T_L1 + T_L2write)
           = T_L1 + 0.05 × T_L2write</code></pre><p>Every store pays the L1 time, and 5% of stores also pay the full drain time.</p>
<p>If the question asks for stall time only (as in the earlier homework):</p>
<pre><code>Write stall time = 0.95 × 0 + 0.05 × T_L2write</code></pre><h3 id="step-3-plug-in-numbers-from-your-earlier-problem">Step 3: Plug in numbers (from your earlier problem)</h3>
<p>L1 hit time = 1 ns, L2 hit time = 15 ns, L2 local miss rate = 20%, L2 miss penalty = 150 ns.</p>
<pre><code>T_L2write = 15 + 0.20 × 150 = 15 + 30 = 45 ns

Write time        = 1 + 0.05 × 45 = 1 + 2.25 = 3.25 ns
Write stall time  = 0.05 × 45     = 2.25 ns</code></pre><h3 id="step-4-exam-traps">Step 4: Exam traps</h3>
<ul>
<li><p>Read the question for what to include. &quot;Stall time only&quot; → 2.25 ns. &quot;Write time to L1&quot; including the access → 3.25 ns. Show the formula so you get credit either way.</p>
</li>
<li><p>The waiting time depends on the stated assumption. In your homework, the store arrives just as the buffer starts writing an entry to L2, so it waits the entire 45 ns. If a problem instead says a store arrives at a random point during the drain, the average wait would be about half of that. Use exactly what the problem states.</p>
</li>
<li><p>The L1 miss rate is not used for writes when the L1 is write-through, no-write-allocate. Every store goes into the buffer whether it hits or misses in L1. If a problem says write-allocate, write misses would also pay an L1 miss penalty.</p>
</li>
<li><p>Don&#39;t forget the L2 miss part of T_L2write. Using only the 15 ns L2 hit time (giving 0.05 × 15 = 0.75 ns) is a common mistake. L2 is write-allocate, so 20% of buffer writes miss in L2 and pay the 150 ns.</p>
</li>
</ul>
<h1 id="span-stylecolororangeamatspan"><span style="color:Orange">AMAT</span></h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/bd7444e2-66c6-4e4f-baeb-ca90c618140e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/9524988d-eeb5-437a-a37c-e6d87236c0e2/image.png" alt=""></p>
<p>The notes write miss_rate(RLL + %dirtyD), which means miss_rate × (RLL + %dirty × D).</p>
<p>The one idea behind every equation</p>
<pre><code>AMAT = (time every access pays) + Σ (how often an extra event happens) × (how long it takes)</code></pre><p>Every access pays its hit time (RH or WH). Then you ask which extra trips to the lower level this access can cause, and how often each one happens. There are only four possible trips:</p>
<ol>
<li><p>Fetching a block (RLL). Happens when a miss brings the block into the cache. Reads always do this. Writes do it only with write-allocate. It happens only on misses, so it is multiplied by miss_rate.</p>
</li>
<li><p>Writing back a dirty victim (D). When a block is brought in, another is thrown out. Only write-back caches have dirty blocks, so D appears only for write-back, only inside the miss term, and only %dirty of the time.</p>
</li>
<li><p>Sending a write down (WLL).</p>
</li>
</ol>
<ul>
<li>Write-through: every write goes down, hit or miss, so WLL is added without miss_rate.</li>
<li>Write-back + no-write-allocate: hits stay in the cache, but a write miss goes straight down, so the term is miss_rate × WLL.</li>
</ul>
<ol start="4">
<li>Write buffer → multiply each write-down cost by (1 − %wb). The buffer absorbs outgoing writes, so the CPU waits only when it is full. This applies to WLL and D only, never RLL. A buffer can hold data going out, but it can&#39;t make data come in faster.</li>
</ol>
<hr>
<h2 id="reads-only-two-versions">Reads: only two versions</h2>
<p>A read miss always brings the block in, whatever the write policy. Write-allocate vs. no-allocate decides what happens on write misses only, so it never changes the read formula. Only one thing matters for reads: can the replaced block be dirty?</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/da7f3f02-5b6a-48bb-9a0c-fd8fec4d88d8/image.png" alt=""></p>
<p>Write-back + no-write-allocate still has the dirty term. No-allocate only means write misses don&#39;t bring blocks in. Write hits still mark blocks dirty, so a later read miss can evict a dirty block.</p>
<hr>
<h2 id="writes-four-versions-one-at-a-time">Writes: four versions, one at a time</h2>
<p><strong>Write-through + write-allocate: WH + WLL + miss_rate × RLL</strong></p>
<ul>
<li>Every write goes to the cache (WH) and down to the lower level (WLL), hit or miss.</li>
<li>On a write miss, the block is also fetched first (miss_rate × RLL), because of allocate.</li>
<li>No D: write-through blocks are never dirty.</li>
</ul>
<p><strong>Write-through + no-write-allocate: WH + WLL</strong></p>
<ul>
<li>Every write goes down (WLL), hit or miss.</li>
<li>On a miss, the block is not fetched, and the write just goes down. That is the same WLL already counted, so a miss costs nothing extra and the formula has no miss_rate at all.</li>
</ul>
<p><strong>Write-back + write-allocate: WH + miss_rate × (RLL + %dirty × D)</strong></p>
<ul>
<li>A write hit stays in the cache and marks the block dirty (just WH).</li>
<li>A write miss behaves exactly like a read miss: fetch the block (RLL), possibly evicting a dirty victim (%dirty × D), then write into it.</li>
<li>That&#39;s why it&#39;s identical to the write-back read formula, with WH in place of RH.</li>
</ul>
<p><strong>Write-back + no-write-allocate: WH + miss_rate × WLL</strong></p>
<ul>
<li>A write hit stays in the cache (just WH).</li>
<li>A write miss sends the write straight down (WLL) and does not fetch the block.</li>
<li>No RLL (nothing fetched) and no D (nothing evicted).</li>
</ul>
<hr>
<h2 id="adding-a-write-buffer-one-rule">Adding a write buffer: one rule</h2>
<p>The write buffer only affects writes going down. Find every WLL and D term and multiply it by (1 − %wb). Leave RH, WH, and RLL alone.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f07fa661-d00e-4bae-9471-b96eca97c1cd/image.png" alt=""></p>
<p>That turns all 8 buffer formulas from your notes into the 8 non-buffer ones with one change each. For write-through reads nothing changes at all, since there is no WLL or D in them.</p>
<p>Where (1 − %wb) comes from: with probability %wb the write drops into the buffer and the CPU waits 0. With probability (1 − %wb) the buffer is full, and the CPU waits the whole write time:</p>
<pre><code>%wb × 0 + (1 − %wb) × WLL = (1 − %wb) × WLL</code></pre><hr>
<h2 id="patterns-to-check-your-answer">Patterns to check your answer</h2>
<p>*<em>질문 ① *</em>미스 때 블록을 가져오나? → miss_rate × RLL</p>
<ul>
<li>읽기 미스는 항상 가져옴</li>
<li>쓰기 미스는 write-allocate일 때만 가져옴</li>
</ul>
<p>*<em>질문 ② *</em>블록을 가져올 때, 밀려나는 블록이 dirty일 수 있나? → + %dirty × D (미스 항 안에)</p>
<ul>
<li>write-back일 때만 해당. write-through는 원본이 항상 최신이라 dirty가 없음</li>
</ul>
<p><strong>질문 ③</strong> 쓰기가 아래로 내려가나? → WLL</p>
<ul>
<li>write-through: 쓰기마다 항상 → WLL (miss_rate를 곱하지 않음)</li>
<li>write-back + no-allocate: 쓰기 미스일 때만 → miss_rate × WLL</li>
</ul>
<p>*<em>질문 ④ *</em>쓰기 버퍼가 있나? → WLL과 D 앞에 (1 − %wb)</p>
<ul>
<li>우편함에 자리가 있으면(%wb) 기다림 0, 꽉 차면(1 − %wb) 끝까지 기다림</li>
<li>RLL에는 절대 붙이지 않음. 우편함은 &quot;보내는 것&quot;만 도와주고, &quot;가져오는 것&quot;은 빨라지게 못 함writes going down (WLL, D), never RLL.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/1bae84a6-42dd-4525-ab4d-2a86b6f4e038/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/b06f715c-7751-4805-917c-f781c6fbb816/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/1e3152e9-c525-4059-82bf-cca6e6154be8/image.png" alt=""></p>
<pre><code>Write through + write allocate
- Read AMAT = RH + miss_rate*RLL
- Write AMAT= WH + WLL + miss_rate*RLL

Write through + no write allocate
- Read AMAT = RH + miss_rate*RLL
- Write AMAT = WH + WLL

Write back + write allocate
- Read AMAT = RH + miss_rate*(RLL+%dirty*D)
- Write AMAT = WH + miss_rate*(RLL+%dirty*D)

Write back + no write allocate
- Read AMAT = RH + miss_rate*(RLL+%dirty*D)
- Write AMAT = WH + miss_rate*WLL

Write through + write allocate + write buffer
- Read AMAT = RH + miss_rate*RLL
- Write AMAT= WH + (1-%wb)*WLL + miss_rate*RLL

Write through + no write allocate + write buffer
- Read AMAT = RH + miss_rate*RLL
- Write AMAT = WH + (1-%wb)* WLL

Write back + write allocate + write buffer
- Read AMAT = RH + miss_rate*(RLL+%dirty* (1-%wb)* D)
- Write AMAT = WH + miss_rate*(RLL+%dirty* (1-%wb)* D)

Write back + no write allocate + write buffer
- Read AMAT = RH + miss_rate*(RLL+% dirty* (1-%wb)* D)
- Write AMAT = WH + miss_rate* (1-%wb)* WLL
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[HW3 - mid term (most important)]]></title>
            <link>https://velog.io/@houston_guy2/HW3</link>
            <guid>https://velog.io/@houston_guy2/HW3</guid>
            <pubDate>Wed, 30 Sep 2026 19:39:15 GMT</pubDate>
            <description><![CDATA[<p>You are building a computer system around a processor with in-order execution that runs at 1 GHz and has a CPI of 1, excluding memory accesses. The only instructions that read or write data from/to memory are loads (20% of all instructions) and stores (5% of all instructions).</p>
<p>The memory system for this computer has a split L1 cache. Both the I-cache and the D-cache are direct mapped and hold 32 KB each. The I-cache has a 2% miss rate and 64 byte blocks, and the D-cache is a write-through, no-write-allocate cache with a 5% miss rate and 64 byte blocks. The hit time for both the Icache and the D-cache is 1ns. The L1 cache has a write buffer. 95% of writes to L1 find a free entry in the write buffer immediately. The other 5% of the writes have to wait until an entry frees up in the write buffer (assume that such writes arrive just as the write buffer initiates a request to L2 to free up its entry and the entry is not freed up until the L2 is done with the request). The processor is stalled on a write until a free write buffer entry is available.</p>
<p>The L2 cache is a unified write-back, write-allocate cache with a total size of 512 KB and a block size of 64-bytes. The hit time of the L2 cache is 15ns. Note that this is also the time taken to write a word to the L2 cache. The local hit rate of the L2 cache is 80%. Also, 50% of all L2 cache blocks replaced are dirty. The 64-bit wide main memory has an access latency of 20ns (including the time for the request to reach from the L2 cache to the main memory, you can consider this is the initial setup time for a memory access), after which any number of bus words may be transferred at the rate of one bus word (64-bit) per bus cycle on the 64-bit wide 100 MHz main memory bus. Assume inclusion between the L1 and L2 caches and assume there is no write-back buffer at the L2 cache. Assume a write-back takes the same amount of time as an L2 read miss of the same size.</p>
<p>While calculating any time values (such as hit time, miss penalty, AMAT), please use ns (nanoseconds) as the unit of time. For miss rates below, give the local miss rate for that cache. By miss penaltyL2, we mean the time from the miss request issued by the L2 cache up to the time the data comes back to the L2 cache from main memory.</p>
<hr>
<h2 id="1-computing-the-amat-average-memory-access-time-for-span-stylecolororangeinstruction-accessesspan">1. Computing the AMAT (average memory access time) for <span style="color:orange">instruction accesses.</span></h2>
<h4 id="a-give-the-values-of-the-following-terms-for-span-stylecolorlightgreeninstruction-accessesspan-hit-timel1-miss-ratel1-hit-timel2-miss-ratel2">a. Give the values of the following terms for <span style="color:lightgreen">instruction accesses</span>. hit timeL1, miss rateL1, hit timeL2, miss rateL2</h4>
<p>only asked instruction accesss so doesn&#39;t need to care D-cache
<img src="https://velog.velcdn.com/images/houston_guy2/post/7d4d12c7-7b23-440e-b03b-02b0b90bda84/image.png" alt=""></p>
<h4 id="b-give-the-formula-for-calculating-miss-penaltyl2-and-compute-the-value-of-miss-penaltyl2">b. Give the formula for calculating miss penaltyL2, and compute the value of miss penaltyL2.</h4>
<pre><code>1 + 0.02 (15+0.2 x 150) = 1.9ns </code></pre><pre><code>step 1
Read the new block from memory (always).
Write back the old block that is being replaced, if it is dirty (sometimes).

step 2
Access latency = 20
Bus is 64 bits wide -&gt; 8 bytes
Bus runs at 100 MHz -&gt; 10ns CCT

How many transfers for one block?
Block size ÷ bus width = 64 bytes ÷ 8 bytes = 8 bus words

How long do those 8 transfers take?
8 bus words × 10 ns = 80 ns

Total to read one block:

Read time = latency + transfer time
          = 20 ns + 80 ns
          = 100 ns</code></pre><p><img src="https://velog.velcdn.com/images/houston_guy2/post/49674ac2-358f-475c-a58c-3114ae09c834/image.png" alt=""></p>
<pre><code>MP L2 = [latency + (block size ÷ bus width) × bus cycle time] × (1 + fraction dirty)
                = [20 + (64 ÷ 8) × 10] × (1 + 0.5)
                = 100 × 1.5
                = 150 ns</code></pre><h4 id="c-give-the-formula-for-calculating-the-amat-for-this-system-using-the-five-terms-whose-values-you-computed-above-and-any-other-values-you-need">c. Give the formula for calculating the AMAT for this system using the five terms whose values you computed above and any other values you need.</h4>
<pre><code>AMAT = hit time L1 + miss rate L1 × (hit time L2 + miss rate L2 × miss penalty L2)</code></pre><h4 id="d-plug-in-the-values-into-the-amat-formula-above-and-compute-a-numerical-value-for-amat-for-instruction-accesses">d. Plug in the values into the AMAT formula above, and compute a numerical value for AMAT for instruction accesses.</h4>
<pre><code>AMAT = hit time L1 + miss rate L1 × (hit time L2 + miss rate L2 × miss penalty L2)</code></pre><hr>
<h2 id="2-computing-the-amat-for-span-stylecolororangedata-readsspan">2. Computing the AMAT for <span style="color:orange">data reads</span></h2>
<h4 id="a-give-the-value-of-miss-rate-l1-for-data-reads">a. Give the value of miss rate L1 for data reads.</h4>
<pre><code>miss rate L1 = 0.05 (5%), the D-cache&#39;s miss rate.</code></pre><h4 id="b-calculate-the-value-of-the-amat-for-data-reads-using-the-above-value-and-other-values-you-need">b. Calculate the value of the AMAT for data reads using the above value, and other values you need.</h4>
<pre><code>AMAT(data read) = 1 + 0.05 × (15 + 0.20 × 150)
                = 1 + 0.05 × 45
                = 1 + 2.25
                = 3.25 ns</code></pre><hr>
<h2 id="3-computing-the-amat-for-span-stylecolororangedata-writesspan">3. Computing the AMAT for <span style="color:orange">data writes.</span></h2>
<p>First, look at the path a write takes. The D-cache is write-through and no-write-allocate, so every store, hit or miss, is placed in the write buffer. The buffer then writes it to L2 in the background. The 5% D-cache miss rate therefore does not affect writes: a write never waits for a block to be brought into L1.</p>
<h4 id="a-give-the-value-of-miss-penaltyl2-for-data-writes">a. Give the value of miss penaltyL2 for data writes.</h4>
<p>A write reaching L2 that misses is handled the same way as a read miss. L2 is write-allocate, so it fetches the whole block from memory, and it is write-back, so the replaced block is dirty 50% of the time and must be written out first, with no write-back buffer.</p>
<p>miss penalty L2 (writes) = 150 ns, the same as for reads.</p>
<h4 id="b-give-the-value-of-write-timel2buff-for-a-write-buffer-entry-being-written-to-the-l2-cache">b. Give the value of write timeL2Buff for a write buffer entry being written to the L2 cache.</h4>
<p>This is the time for one write-buffer entry to be written into L2, including the L2 misses:</p>
<pre><code>write time L2Buff = hit time L2 + miss rate L2 × miss penalty L2
                  = 15 + 0.20 × 150
                  = 15 + 30
                  = 45 ns</code></pre><p>write time L2Buff = 45 ns (15 ns is the given time to write a word into L2).</p>
<h4 id="c-calculate-the-value-of-the-amat-for-data-writes-using-the-above-two-values-and-any-other-values-that-you-need-only-include-the-time-that-the-processor-will-be-stalled-hint-there-are-two-cases-to-be-considered-here-depending-upon-whether-the-write-buffer-is-full-or-not">c. Calculate the value of the AMAT for data writes using the above two values, and any other values that you need. Only include the time that the processor will be stalled. Hint: There are two cases to be considered here depending upon whether the write buffer is full or not.</h4>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/bf3f132c-b288-46a8-a7d5-f4b95c3a465d/image.png" alt=""></p>
<p>There are two cases:</p>
<p>Case 1: buffer has a free entry (95% of writes).
The store goes into the buffer immediately and the processor continues. Stall = 0 ns.</p>
<p>Case 2: buffer is full (5% of writes).
The problem says the write arrives just as the buffer starts sending an entry to L2, and that entry isn&#39;t free until L2 finishes. The processor waits the entire L2 write. Stall = 45 ns.</p>
<pre><code>AMAT(data write) = 0.95 × 0 + 0.05 × 45
                 = 2.25 ns

AMAT for data writes = 2.25 ns</code></pre><p>(Some instructors also count the 1 ns L1 write access in every case. That gives 0.95 × 1 + 0.05 × (1 + 45) = 3.25 ns. The problem says to include only stall time, so 2.25 ns is the intended answer.)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Increasing Cache BandWidth (Not included in mid term)]]></title>
            <link>https://velog.io/@houston_guy2/Increasing-Cache-BandWidth</link>
            <guid>https://velog.io/@houston_guy2/Increasing-Cache-BandWidth</guid>
            <pubDate>Wed, 30 Sep 2026 16:18:15 GMT</pubDate>
            <description><![CDATA[<pre><code>Increasing Cache BandWidth
1. Pipelined caches
    A. Pipelining
    B. Miss Status Handling Register (MSHR)
2. Nonblocking caches
3. Multibanked caches</code></pre><h1 id="1-pipelined-caches">1. Pipelined caches</h1>
<h2 id="a-pipelining">A. Pipelining</h2>
<p>A processor&#39;s clock cycle can be no shorter than its slowest single step (the critical path). A cache access is one of the slowest steps: decode the index, read the arrays, compare tags, select the data. If the whole access has to fit in one cycle, it limits the clock speed of the entire CPU.</p>
<p><strong>The idea: split the cache access into stages</strong></p>
<p>Pipelining the cache works like an assembly line. The access is cut into stages, for example index → read → tag compare → data out, with latches between them. Each stage is short, so the clock can be faster. A new access can enter the cache every cycle, while earlier ones are still in later stages.</p>
<p>Two things change, and it&#39;s important to keep them apart:</p>
<ul>
<li>Bandwidth goes up: more accesses finish per nanosecond, one per (shorter) cycle.</li>
<li>Latency does not improve: each access still takes about the same real time, a little more because of latch overhead. Measured in cycles, a hit now takes k cycles instead of 1.</li>
</ul>
<p>1stage
<img src="https://velog.velcdn.com/images/houston_guy2/post/8ce8e143-d2cd-49d3-adca-71d49d36c0a9/image.png" alt=""></p>
<p>2stages
<img src="https://velog.velcdn.com/images/houston_guy2/post/4eff77d5-af6e-451d-afa4-913c4aa6009a/image.png" alt=""></p>
<p>3stages
<img src="https://velog.velcdn.com/images/houston_guy2/post/71f4ad80-fe63-47b1-bda5-498c7b33d5a1/image.png" alt=""></p>
<p>4stages
<img src="https://velog.velcdn.com/images/houston_guy2/post/91650a7a-b8fd-4f8e-bf97-ea00576a6d47/image.png" alt=""></p>
<p>What to notice as you go from 1 to 4 stages:</p>
<p>The clock cycle shrinks, from about 2.1 ns to 0.6 ns, so the whole CPU can run faster.
Bandwidth more than triples, because a new load starts every cycle.
Hit latency in real time rises slightly, from 2.1 ns to 2.4 ns, because each latch adds overhead. Pipelining never makes a single access faster.
Hit latency in cycles grows from 1 to 4, which is where the costs come from.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/ee48db72-932b-4f00-b639-5a9845c1ce59/image.png" alt=""></p>
<p>Each step allowed a much higher clock rate. The Pentium 4 was designed for very high GHz, which is why it pipelined the cache (and everything else) so deeply.</p>
<p>The two costs in your notes</p>
<ol>
<li><p>More cycles between issuing a load and using its data (load-use delay)
With a 4-cycle hit, an instruction that needs the loaded value must wait extra cycles, the yellow &quot;wait&quot; boxes in the widget:</p>
<pre><code>lw   r1, 0(r2)     # load, takes k cycles
add  r3, r1, r4    # needs r1, so it stalls k-1 cycles</code></pre><p>The processor hides this by doing independent work in the gap. The compiler schedules other instructions between the load and its use, or out-of-order execution (which the Pentium Pro introduced) runs later instructions that don&#39;t depend on the load. If there&#39;s nothing independent to run, the stall shows up.</p>
</li>
<li><p>Greater penalty on mispredicted branches
The instruction cache is pipelined too, so fetching instructions takes more stages, and the whole CPU pipeline gets longer. When a branch is mispredicted, every instruction fetched after it is wrong and must be thrown away, and the pipeline must refill from the correct address. More stages mean more wasted cycles per mistake. On a very deep pipeline like the Pentium 4&#39;s, this penalty is large, so good branch prediction becomes essential.</p>
</li>
</ol>
<blockquote>
<p>Pipelining the cache trades latency in cycles for higher clock rate and bandwidth. It pays off when the processor can keep the pipeline full with independent instructions and correct branch predictions, and it hurts when it can&#39;t.</p>
</blockquote>
<h2 id="b-miss-status-handling-register-mshr">B. Miss Status Handling Register (MSHR)</h2>
<p>The bookkeeping hardware: MSHRs</p>
<p>A Miss Status Handling Register (MSHR) records one outstanding miss. There is one MSHR per cache block being fetched, and inside it a small list of every load and store waiting for that block.</p>
<p>Fields of one MSHR:
<img src="https://velog.velcdn.com/images/houston_guy2/post/f83116a3-d6db-4efb-a1ce-a8b5bf28e99c/image.png" alt=""></p>
<p>Fields of each load/store entry:
<img src="https://velog.velcdn.com/images/houston_guy2/post/3d1b2e96-62dc-46ac-ac82-818c0935d55f/image.png" alt=""></p>
<p>Two kinds of misses use these:</p>
<p>Primary miss: the first miss to a block. A new MSHR is allocated and a memory request is sent.
Secondary miss: a later miss to a block that is already being fetched. No new request is sent; an entry is added to the existing MSHR.
How it operates, step by step</p>
<p>Use Next to walk through a sequence of accesses. The cache has 2 MSHRs, each with room for 3 waiting loads or stores, and 64-byte blocks.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f87bf432-e6a0-400c-9efa-b6c8f58dee54/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/28df1964-b020-48eb-b049-bdf7544d762c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/a76d71d0-c4e2-414b-bc46-8bb45ea8b6fe/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/c53a62ff-488b-4830-ba63-aaabd7ff47d3/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/83955d64-2d26-48d2-8869-fd0f3375b21b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/9d017c11-8b12-4c98-a31c-55fcb0dc15df/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/cdb02de6-7176-4090-855a-4510b8d89694/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/6efaafac-12fb-4703-9e17-f3d49f17ab4b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/5bcc7aad-d272-464b-b779-bcce7995bb45/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/d0bdb76c-81e7-4d4d-9b5a-05662cd5fd88/image.png" alt=""></p>
<p><strong>The operation rules from your slide, matched to the walkthrough</strong>
On a cache miss:</p>
<ol>
<li><p>Search the MSHRs for the same block address.</p>
<ul>
<li>Found (secondary miss, step 3): just add a new load/store entry to that MSHR. No new memory request is sent.</li>
<li>Not found (primary miss, steps 1 and 4): allocate a free MSHR, write the block address and the first load/store entry, and send the request to memory (Issued = 1).</li>
</ul>
</li>
<li><p>If no MSHR is free, or the MSHR&#39;s entries are full, stall (step 5). The number of MSHRs sets how many misses can overlap.</p>
</li>
</ol>
<p><strong>When one word (or sub-block) of the line arrives (steps 6 and 7):</strong></p>
<ol>
<li>Check which entries are waiting for that word, using their block offsets.</li>
<li>Forward the data to the CPU. A load writes its destination register; a store merges its store-buffer data into the block.</li>
<li>Mark those entries invalid.</li>
<li>Write the word into the cache.</li>
</ol>
<p>When the last word of the line arrives (step 8):</p>
<ul>
<li>Mark the MSHR invalid, so it is free for the next miss.</li>
</ul>
<h1 id="2-nonblocking-caches">2. Nonblocking caches</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/86ac45b0-82d3-457b-82b3-f06a7e0ae9a1/image.png" alt=""></p>
<p>A non-blocking cache (also called lockup-free) keeps working while it waits for a miss. The idea starts with what the CPU can do during a miss.</p>
<p>Why it&#39;s useful: out-of-order execution</p>
<p>An in-order CPU must stop when a load misses, because the next instruction may need that data. An out-of-order CPU can look ahead and run later instructions that don&#39;t depend on the missing data. Many of those are also loads and stores, so they need the data cache.</p>
<p>With a normal blocking cache, the cache refuses all requests until the miss is resolved. The CPU has independent work ready, but the cache won&#39;t serve it, so the CPU sits idle anyway.</p>
<p>A non-blocking cache keeps serving requests during a miss. The timeline below shows four loads: A (miss), B (hit), C (miss), D (hit). A hit takes 1 cycle, and a miss takes 10.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/aa9fb286-8dee-4f1b-9a6e-38152303d402/image.png" alt=""></p>
<h3 id="three-levels-of-non-blocking">Three levels of non-blocking</h3>
<p>Blocking: while A is missing, the cache ignores every request. B and D are hits, but they still have to wait.</p>
<p>Hit under miss: the cache serves hits while one miss is pending. B and D no longer wait for A. A second miss (C) still stalls the cache until A finishes, because only one miss can be tracked at a time.</p>
<p>Miss under miss (hit under multiple miss): the cache tracks several misses at once, so C&#39;s memory request is sent while A&#39;s is still in progress. Their waiting times overlap, and that gives the biggest saving.</p>
<p>The miss penalty for each individual miss doesn&#39;t change; A still takes 10 cycles. What drops is the effective miss penalty, the time the CPU actually loses, because useful work and other misses fill the waiting time.</p>
<p>The costs (from your notes):</p>
<ul>
<li><p>More complex cache controller. It must track many outstanding misses at once, remember which loads and stores are waiting for each one, and deliver data to the right place when it returns.</p>
</li>
<li><p>The lower level must accept several requests at once. If memory can handle only one request at a time, the second miss waits anyway. That&#39;s why memory is split into multiple banks, so different misses can be served by different banks in parallel.</p>
</li>
</ul>
<h1 id="3-multibanked-caches">3. Multibanked caches</h1>
<p>A banked cache is split into several independent pieces (banks), and each bank can serve its own access in the same cycle. Here is what that means and how addresses are spread across the banks.</p>
<p><strong>The problem: one cache, one access at a time</strong>
A cache built as a single block of memory can handle one access per cycle. That becomes a bottleneck when many requests arrive together:</p>
<ul>
<li>An out-of-order CPU may issue several loads and stores per cycle.</li>
<li>A non-blocking cache (the previous topic) has misses returning while new hits are being served.</li>
<li>A multicore/multithreaded chip has many cores sharing one L2.</li>
</ul>
<p><strong>The solution: split the cache into independent banks</strong>
Divide the cache into several banks. Each bank has its own decoder and data array, so it can do its own access. If four requests go to four different banks, all four are served in the same cycle, giving four times the bandwidth.</p>
<p>Example: Sun T1 (&quot;Niagara&quot;). This chip has 8 cores running 32 threads, all sharing one L2. A single-ported L2 would be swamped, so it is split into 4 banks that work in parallel.</p>
<p><strong>Which address goes to which bank? Sequential interleaving</strong>
Banking helps only if requests spread out across the banks. If they all land in the same bank, they must wait in line, which is called a bank conflict.</p>
<p>The simple mapping from your notes spreads consecutive blocks across banks in rotation:</p>
<pre><code>bank number      = block address mod (number of banks)
position in bank = block address ÷ (number of banks)</code></pre><p>With 4 banks, blocks 0, 1, 2, 3 go to banks 0, 1, 2, 3, then block 4 wraps back to bank 0, and so on. Since programs often access neighboring blocks (arrays, instruction streams), consecutive requests naturally hit different banks.</p>
<p>To get the block address from a byte address, divide by the block size first. For example, with 64-byte blocks, byte address 448 → block 448 ÷ 64 = 7 → bank 7 mod 4 = bank 3.</p>
<p>Pick an access pattern below (or click blocks to choose your own) and see how many cycles it takes to serve the requests.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/d8977cc8-4dd2-4f6b-b153-bfcab67afdb8/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/9a5cf607-e9d2-4645-90b2-aabf4537a870/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/1b490845-f37f-4c9d-8fa1-0ce1c125c93a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f9234559-2117-49ea-9e01-309abfd6f78f/image.png" alt=""></p>
<p>The patterns show the point from your notes, that the address-to-bank mapping decides how well banking works. Sequential accesses spread out perfectly. Strides that are a multiple of the number of banks (like stride 4 with 4 banks) all pile into one bank and lose the whole benefit. That&#39;s why some designs use a hashed mapping (for example, XOR-ing some address bits) to break up such patterns.</p>
<h4 id="dont-confuse-ways-and-banks">Don&#39;t confuse ways and banks</h4>
<p>Both split a cache into parts, but for different reasons:
<img src="https://velog.velcdn.com/images/houston_guy2/post/372a648a-8304-499b-a296-e1c620117720/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Reducing Hit times (Not included in mid term)]]></title>
            <link>https://velog.io/@houston_guy2/Reducing-Hit-times</link>
            <guid>https://velog.io/@houston_guy2/Reducing-Hit-times</guid>
            <pubDate>Wed, 30 Sep 2026 16:07:26 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/houston_guy2/post/c4747654-433f-49c8-b41a-1df5e839633d/image.png" alt=""></p>
<h1 id="reducing-hit-time">Reducing Hit Time</h1>
<p>Hit time is the time to find data that is in the cache: use the index to pick a set, read its tag(s) and data, compare tags, then choose the right block and send it to the CPU. Both techniques here make that path shorter.</p>
<h2 id="1-small-and-simple-caches">1. Small and Simple Caches</h2>
<p>Small helps because a smaller memory array has shorter wires and fewer rows to decode, so indexing and reading are faster. This is why L1 size often stays flat across processor generations (as in the AMD K6, Athlon, Opteron example). Making L1 bigger would slow every access, and hit time sets the clock cycle.</p>
<p>Simple (direct-mapped) helps because each address has only one possible location. The cache can send the data to the CPU right away, while the tag check runs in parallel. If the tag turns out not to match, the data is thrown away. A set-associative cache can&#39;t do this. It must finish all its tag comparisons before the multiplexer knows which way&#39;s data to send.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/4dbc11bd-6d90-4400-aca8-62dc7c9f7f56/image.png" alt=""></p>
<h2 id="2-way-prediction">2. Way Prediction</h2>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/29f0091f-2b59-4fb4-aaa2-6a6571567803/image.png" alt=""></p>
<p>The problem it solves</p>
<p>There are two options, and each has a weakness:</p>
<p>Direct-mapped: fast hit, because there is one possible location and the data can be sent before the tag check finishes. But it has more conflict misses.
4-way set-associative: fewer conflict misses, but slower hit, because it must compare 4 tags and then use the mux to choose the data.</p>
<p>Way prediction keeps the set-associative cache, and its low miss rate, but guesses which way holds the data. If the guess is right, the access behaves like a direct-mapped one.</p>
<p>&quot;Use load PC or XOR the load src reg and load offset to index the prediction table&quot;
The prediction table is a small memory that remembers which way each load found its data in last time. It has to give an answer before the cache access starts, but the real memory address (base register + offset) isn&#39;t finished computing yet. So the table is indexed by something available earlier:</p>
<ul>
<li>the PC of the load instruction. The same load instruction tends to hit the same way again, for example inside a loop.</li>
<li>or base register XOR offset, a quick stand-in for the address. XOR has no carry chain, so it&#39;s much faster than the full addition.</li>
</ul>
<p>correct prediction
<img src="https://velog.velcdn.com/images/houston_guy2/post/f3455976-8033-443f-9041-8de9159e6d58/image.png" alt=""></p>
<p>Wrong way, hit elsewhere 
<img src="https://velog.velcdn.com/images/houston_guy2/post/389c4602-d8e5-4ac2-b45b-ca0f82fbc2a6/image.png" alt=""></p>
<p>Miss in all ways
<img src="https://velog.velcdn.com/images/houston_guy2/post/79a69429-bf54-4f16-9b10-f0141dd20b59/image.png" alt=""></p>
<p>Random access(85% accurate)
<img src="https://velog.velcdn.com/images/houston_guy2/post/18317305-483c-4d61-852c-a65ef3559844/image.png" alt=""></p>
<p>With 85% accuracy, most hits run at direct-mapped speed. You can estimate the average hit time like this:</p>
<p>Average hit time ≈ 0.85 × 1 cycle + 0.15 × 2 cycles = 1.15 cycles</p>
<p>The miss rate stays that of a 4-way cache, because every way is still checked before declaring a miss. Only the order of checking changes.</p>
<h4 id="any-other-benefit-of-way-prediction--power-savings">&quot;Any other benefit of way prediction?&quot; — power savings</h4>
<p>If you click random a few times, the average tag compares per access drops far below 4. A normal 4-way cache powers up all 4 tag comparators and reads all 4 data blocks on every access, then throws away 3 of them. With way prediction, a correct guess needs only 1 tag compare and 1 data array read. That is roughly a quarter of the dynamic energy per access in a 4-way cache. The saving matters a lot in L1 caches, which are accessed almost every cycle, and in battery-powered chips. Some designs go further with way selection: they read the data array of only the predicted way.</p>
<h3 id="example">Example</h3>
<p>just read, not in the exame</p>
<p>(a) For a 1024 KB L2 cache with 64-byte blocks and 8-way set associativity, how many way prediction table entries are needed?</p>
<pre><code>1024 KB, 64-byte blocks, 8-way
Blocks = 1024 KB ÷ 64 B = 2²⁰ ÷ 2⁶ = 2¹⁴ = 16,384 blocks
Sets = 16,384 ÷ 8 = 2¹¹ = 2,048 sets
Bits per entry = log₂ 8 = 3 bits
Answer: 2,048 entries, each 3 bits, for a total of 2,048 × 3 = 6,144 bits (6 Kb).</code></pre><p>(b) For an 8 MB L2 cache with 128-byte blocks and 2-way set associativity, how many way prediction table entries are needed?</p>
<pre><code>(b) 8 MB, 128-byte blocks, 2-way
Blocks = 8 MB ÷ 128 B = 2²³ ÷ 2⁷ = 2¹⁶ = 65,536 blocks
Sets = 65,536 ÷ 2 = 2¹⁵ = 32,768 sets
Bits per entry = log₂ 2 = 1 bit

Answer: 32,768 entries, each 1 bit, for a total of 32,768 bits (32 Kb).</code></pre><p>(c) What is the difference in the way that the processor with only 8Kb way prediction table will support the cache in part (a) versus the cache in part (b)? </p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/11572d39-71ef-430c-bc20-e276bc1fa3c2/image.png" alt=""></p>
<p>Cache (a) is fully supported. 6,144 bits fits in 8,192, so every one of the 2,048 sets gets its own 3-bit predictor, with about 2 Kb left unused. Prediction accuracy is as good as the predictor allows.</p>
<p>Cache (b) is only partially supported. The table can hold only 8,192 one-bit entries, but there are 32,768 sets. The table is indexed with only the low 13 bits of the 15-bit set index, so 4 different sets share each entry (aliasing). When those sets are used in the same period and their blocks sit in different ways, they overwrite each other&#39;s prediction. This causes more mispredictions, and each one costs an extra cycle to check the other way, so the average hit time rises.</p>
<p>The cache still works correctly in (b). A prediction is only a hint, and the tag comparison always checks it. A wrong guess costs time, not correctness.</p>
<p>If your course means &quot;8K entries&quot; rather than 8K bits, the conclusion is the same. Cache (a) needs only 2,048 of the 8,192 entries (with 3-bit entries), so it fits. Cache (b) needs 32,768 entries, so each entry is still shared by 4 sets.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Victim Caches]]></title>
            <link>https://velog.io/@houston_guy2/Victim-Caches</link>
            <guid>https://velog.io/@houston_guy2/Victim-Caches</guid>
            <pubDate>Wed, 30 Sep 2026 15:48:33 GMT</pubDate>
            <description><![CDATA[<h1 id="2-victim-caches">2. Victim Caches</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/8ab04062-c7ff-4edd-96ff-d376b649af7e/image.png" alt=""></p>
<ul>
<li>Victim cache holds data that has been deleted from the cache, in case it is needed again</li>
<li>This is fully associative cache and can help to reduce misses with direct-mapped or set associative caches</li>
</ul>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/31adbf54-be30-4a9c-969a-3b5ceaea0656/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/c06ba3fd-3a4c-4cfe-973c-ec6123474c69/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/259f51a0-ad9d-4676-8d7c-cf698cb2f11a/image.png" alt=""></p>
<p>A victim cache is a small buffer that sits between a cache and the next level of the memory hierarchy. It catches blocks the main cache has just evicted, so that if the processor asks for one of them again soon, it can be recovered quickly instead of fetched from the slower level below.</p>
<h3 id="how-it-works">How it works</h3>
<ol>
<li><p>On a cache miss, the CPU looks in the main cache and, at the same time (or on the very next step), in the victim cache.</p>
</li>
<li><p>If the block is in the victim cache (a victim hit), it is swapped with the block currently occupying that line in the main cache. The evicted block goes into the victim cache, and the requested block moves into the main cache. The penalty is only a cycle or two, far less than going to L2 or memory.</p>
</li>
<li><p>If it misses in both, the block is fetched from the next level and placed in the main cache. Whatever it displaces is moved into the victim cache, pushing out the oldest entry (typically FIFO or LRU replacement).</p>
</li>
</ol>
<p>So blocks flow one way in the normal case: main cache → victim cache → discarded. The swap on a victim hit is the exception.</p>
<h3 id="key-properties">Key properties</h3>
<ul>
<li>Small: typically 4 to 16 entries, each holding a full cache block.</li>
<li>Fully associative: any evicted block can go in any entry, and lookups compare against all tags in parallel. That is only affordable because it is so small.</li>
<li>Holds only evicted blocks: it is not filled directly from memory, so it complements the main cache rather than duplicating it.</li>
<li>Exclusive in spirit: a block lives in either the main cache or the victim cache, not both, thanks to the swap.</li>
</ul>
<h3 id="the-three-cases-to-remember">The three cases to remember:</h3>
<ul>
<li>Main cache hit: normal fast path, nothing else happens.</li>
<li>Main miss, victim hit: swap the two blocks, so the requested block moves into the main cache and the evicted one takes its place in the victim cache.</li>
<li>Miss in both: fetch from the next level into the main cache, and the displaced block goes into the victim cache (the oldest victim entry is dropped if it&#39;s full).</li>
</ul>
<p>only after update victim cache evict</p>
<p>RLL = Read from Lower Level (fetching a block from memory) and WLL = Write to Lower Level (sending data down to memory). These are the two kinds of memory traffic that write policies and victim caches change. If your course defines them differently, tell me and I&#39;ll adjust.</p>
<h2 id="the-four-policy-terms">The four policy terms</h2>
<p>Two separate questions decide how a cache handles writes:</p>
<ol>
<li>On a write hit, when does memory get updated?</li>
</ol>
<ul>
<li>Write-back (WB): only the cache is updated, and the block is marked dirty. Memory is updated later, when the dirty block is evicted.</li>
<li>Write-through (WT): the cache and memory are both updated on every write. Blocks are never dirty.</li>
</ul>
<ol start="2">
<li>On a write miss, do we bring the block into the cache?</li>
</ol>
<ul>
<li>Write-allocate (WA): yes. Read the block from memory (an RLL), then write it in the cache.</li>
<li>No-write-allocate (NWA): no. Send the write straight to memory (a WLL) and leave the cache unchanged.</li>
</ul>
<p>These are usually paired. Write-back and write-allocate go together because both try to do writes in the cache instead of in memory, and allocating means a later write to the same block will be a hit. Write-through works naturally with no-write-allocate: every write goes to memory anyway, so loading the block on a write miss gains little</p>
<h2 id="where-the-victim-cache-fits">Where the victim cache fits</h2>
<p>The victim cache sits between the main cache and memory and catches evicted blocks. Whether those blocks are dirty depends on the write policy:</p>
<p>With WB-WA: evicted blocks may be dirty, so each victim-cache entry needs its own dirty bit. The victim cache becomes a waiting area for dirty data. A dirty block goes to memory (WLL) only when it is pushed out of the victim cache. If it is reused first, the write-back never happens.
With WT-NWA: every block is clean, because memory is always up to date. A block leaving the victim cache is simply discarded, with no WLL. The victim cache saves only reads (RLLs).</p>
<p>In both policies, a victim-cache hit is a swap. It saves an RLL, and in WB it can also save a WLL.</p>
<p>Pick a policy and a situation below to see which paths are used and how many RLLs and WLLs happen.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/14be1b40-af24-47de-929e-79cfd70cebe4/image.png" alt=""></p>
<h2 id="takeaways-for-exams">Takeaways for exams</h2>
<ul>
<li><p>WB-WA pays for writes lazily. Repeated writes to one block cost zero WLL until that block finally leaves the victim cache. The victim cache gives dirty blocks a second chance, so it can save both RLLs and WLLs.</p>
</li>
<li><p>WT-NWA pays one WLL on every write, hit or miss, and its victim cache can only save RLLs. In return, memory is always up to date and evictions are free. Real designs add a write buffer so the CPU doesn&#39;t stall waiting for each WLL.</p>
</li>
<li><p>In both policies, a WB-WA write miss costs an RLL (allocate), while a WT-NWA write miss costs a WLL (bypass).</p>
</li>
<li><p>Large last-level caches are often managed the same way. L3 is sometimes run as a victim cache, filled only with lines displaced from L2 (for example, AMD Barcelona and Apple A9). Handling dirty lines is a key design issue there too.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[VLSI Power]]></title>
            <link>https://velog.io/@houston_guy2/VLSI-Power</link>
            <guid>https://velog.io/@houston_guy2/VLSI-Power</guid>
            <pubDate>Tue, 29 Sep 2026 22:16:57 GMT</pubDate>
            <description><![CDATA[<p>VLSI Power</p>
<p>Vdd -&gt; Capacitor -&gt; Gnd</p>
<p>Dynamic power</p>
<ul>
<li>capacitance swithcing</li>
<li>crowbar current</li>
</ul>
<p>Static power</p>
<ul>
<li>Leakage current</li>
</ul>
<p>relation with energy and delay</p>
<p>Low swing circuit
idea: let&#39;s change unitil vdd/3 so that we can save power
cons: </p>
<ul>
<li>we can not use normal gate</li>
<li>weak at noise, can not tolerate noise, noise sensitive</li>
</ul>
<p>Gated clocks</p>
<ul>
<li>stop the clock and put the logic to sleep</li>
<li>put the and gate with enable signal </li>
</ul>
<p>Increase length of transistors</p>
<ul>
<li>but sacrifice speed</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Midterm-1]]></title>
            <link>https://velog.io/@houston_guy2/Midterm-1</link>
            <guid>https://velog.io/@houston_guy2/Midterm-1</guid>
            <pubDate>Tue, 29 Sep 2026 05:13:35 GMT</pubDate>
            <description><![CDATA[<h1 id="1">1.</h1>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/d94e067f-264b-4ef6-a19f-6cedf331c846/image.png" alt=""></p>
<p>Two NMOS are connected as shown here. What mode of operation (cutoff, linear, saturation) is M2 in? Vt for both MOS is 0.6V. Useful info: The side with the arrow is the source side, Cutoff: Vgs&lt;Vt, Linear: Vgs&gt;Vt &amp; Vds&lt;Vgs-Vt, Saturation: Vgs&gt;Vt &amp; Vds&gt;Vgs-Vt)</p>
<h1 id="2">2.</h1>
<p>For some application, it is desired that the high-to-low transition must be high speed while the low-to high transition is of little consequence. What change(s) will you make to your inverter to account for this requirement? </p>
<h1 id="3">3.</h1>
<p>Implement the Pull-down-network (PDN) only, for the function: 
<img src="https://velog.velcdn.com/images/houston_guy2/post/62b8130d-b9c9-443e-8940-e97714a9819f/image.png" alt=""></p>
<p>No need to size. </p>
<h1 id="4">4.</h1>
<p>For a new Technology, it was determined that 1:2 sizing is optimal(Not 2:1). with this new information. size your design from Q3.</p>
<h1 id="5">5.</h1>
<p>Given the following PDN (Assume that there is also a complementary PUN, that is not showed here), what will be the output(0 or 1) when inputs are</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/61025d5a-0054-4163-a6d0-b0a93717f0eb/image.png" alt=""></p>
<p>a. (A,B,C,D,E) = (1,1,0,0,0): Output:0
b. (A,B,C,D,E) = (1,0,1,0,1): Output:0
c. (A,B,C,D,E) = (1,0,1,1,0): Output:1</p>
<h1 id="6">6.</h1>
<p>Between a 2-input NAND and 2-input NOR, which will consume more dynamic power and why (voltage, and frequency are equal in both cases, the output of both gates is connected to 2:1 inverters)?
(Useful info: Paynamic = aCV&#39;f, a: Switching factor, C: load capacitance, V: Voltage, f; frequency)</p>
<blockquote>
<p>The NOR gate requires larger PMOS transistors because its PMOS devices are connected in series, which increases the switched capacitance and therefore increases dynamic power.</p>
</blockquote>
<h1 id="7">7.</h1>
<p>What is the logical (LE) for a 2-input NAND and 2 input NOR gate if 3:1 sizing is followd in a new technology. Useful info: LE is the ratio of the input capacitance of a given gate to that of an inverter after sizing.</p>
<h1 id="8">8.</h1>
<p>Fill up the truth table for the following circuit. What function is being implemented? </p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/b1606805-66c4-40c1-9447-c9f5eb7bca6f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Design L1, L2 caches]]></title>
            <link>https://velog.io/@houston_guy2/Design-L1-L2-caches</link>
            <guid>https://velog.io/@houston_guy2/Design-L1-L2-caches</guid>
            <pubDate>Mon, 28 Sep 2026 16:06:45 GMT</pubDate>
            <description><![CDATA[<p>The main idea is that L1 and L2 have different jobs, so they are intentionally designed differently.</p>
<p>differently.
$\boxed{\text{L1: optimize for speed}}$</p>
<p>$\boxed{\text{L2: optimize for capacity + lower miss rate}}$</p>
<p>That one idea explains most of the differences in your slide.</p>
<table>
<thead>
<tr>
<th>Design aspect</th>
<th>Typical L1 design</th>
<th>Typical L2 design</th>
<th>Why?</th>
</tr>
</thead>
<tbody><tr>
<td>Size</td>
<td>Small</td>
<td>Larger</td>
<td>L1 must be extremely fast; L2 can trade speed for capacity</td>
</tr>
<tr>
<td>Number of sets</td>
<td>Fewer</td>
<td>More</td>
<td>Larger cache naturally has more sets</td>
</tr>
<tr>
<td>Associativity</td>
<td>Lower</td>
<td>Higher</td>
<td>L1 wants fast lookup; L2 wants fewer conflict misses</td>
</tr>
<tr>
<td>Block size</td>
<td>Small/moderate</td>
<td>Same or sometimes larger</td>
<td>Larger blocks exploit spatial locality but increase miss penalty</td>
</tr>
<tr>
<td>Split vs unified</td>
<td>Usually split I-cache + D-cache</td>
<td>Usually unified</td>
<td>L1 needs parallel instruction/data access; L2 benefits from flexible capacity</td>
</tr>
<tr>
<td>Ports</td>
<td>Often multiple/banked</td>
<td>May be fewer/slower</td>
<td>L1 bandwidth matters more</td>
</tr>
<tr>
<td>Replacement policy</td>
<td>Simple</td>
<td>More sophisticated</td>
<td>L2 can afford extra logic</td>
</tr>
<tr>
<td>Write policy</td>
<td>Usually D-cache write-back</td>
<td>Usually write-back</td>
<td>Reduces traffic to lower levels</td>
</tr>
<tr>
<td>Inclusion</td>
<td>Depends on architecture</td>
<td>Inclusive/exclusive/non-inclusive</td>
<td>Determines whether the same block can exist at multiple levels</td>
</tr>
</tbody></table>
<h2 id="1-cache-size">1. Cache Size</h2>
<p>L1 is normally small because it must be accessed very quickly.
Example:
$L1=32KB$
$L2=512KB$</p>
<p>$\text{Small cache} \rightarrow \text{faster}$</p>
<p>$\text{Large cache} \rightarrow \text{lower miss rate, but slower}$</p>
<h2 id="2-number-of-sets">2. Number of Sets</h2>
<p>Remember:</p>
<p>$\boxed{#Sets= \frac{CacheSize}{BlockSize\times Associativity}}$</p>
<p>Example:
L1:</p>
<p>$32KB,\ 64B,\ 4\text{-way}$</p>
<p>$Sets= \frac{32768}{64\times4} = 128$</p>
<p>L2:</p>
<p>$512KB,\ 64B,\ 8\text{-way}$</p>
<p>$Sets= \frac{524288}{64\times8} = 1024$</p>
<p>So L2 usually has many more sets.
The number of sets affects the address:</p>
<p>$\boxed{IndexBits=\log_2(Sets)}$</p>
<h2 id="3-associativity">3. Associativity</h2>
<p>Associativity tells us how many cache lines are inside each set.</p>
<ul>
<li>Direct mapped = 1-way</li>
<li>2-way = 2 cache lines per set</li>
<li>4-way = 4 cache lines per set</li>
<li>8-way = 8 cache lines per set</li>
</ul>
<p>Higher associativity usually reduces conflict misses because a memory block has more possible locations inside its set.</p>
<p>However, higher associativity also means:</p>
<ul>
<li>more tag comparisons</li>
<li>more hardware</li>
<li>potentially longer hit time
So L1 often uses relatively modest associativity because hit time is very important.
L2 can often use higher associativity because reducing misses is more important there.</li>
</ul>
<h2 id="4-block-size">4. Block Size</h2>
<p>A block is the amount of data transferred between memory levels at one time.
Typical examples:</p>
<ul>
<li>32B</li>
<li>64B</li>
<li>128B
A larger block can help because of spatial locality.
If the CPU accesses one address, it may soon access nearby addresses, so bringing nearby data together can reduce compulsory misses.
However, a larger block also has disadvantages:</li>
<li>larger miss penalty</li>
<li>more transfer time</li>
<li>fewer cache lines for the same cache capacity</li>
<li>possibly more capacity or conflict misses if the block becomes too large
So block size is a trade-off.</li>
</ul>
<h2 id="5-split-cache-vs-unified-cache">5. Split Cache vs Unified Cache</h2>
<p>L1 is commonly split into:</p>
<ul>
<li>L1 instruction cache</li>
<li>L1 data cache</li>
</ul>
<p>The instruction cache stores instructions.
The data cache stores data used by loads and stores.
The reason for splitting them is bandwidth.</p>
<p>The CPU may need to do both of these in the same cycle:</p>
<ul>
<li>fetch an instruction</li>
<li>access data
With separate caches, both accesses can happen at the same time.
So:</li>
<li>instruction fetch → L1 I-cache</li>
<li>load/store → L1 D-cache
L2 is commonly unified.
That means instructions and data share the same L2 capacity.
This allows the space to be used more flexibly.
In general:</li>
<li>L1 → often split</li>
<li>L2 → usually unified</li>
<li>L3 → usually unified</li>
</ul>
<h2 id="6-multiple-ports">6. Multiple Ports</h2>
<p>A cache port is an access path into the cache.
A single-port cache can usually handle only one access at a time.
If a unified cache must handle both:</p>
<ul>
<li>instruction fetch</li>
<li>data access
in the same cycle, one access may have to wait.
This is why some problems say a load/store requires one extra cycle in a single-port unified cache.
A split L1 cache naturally reduces this conflict because instruction and data accesses use different caches.</li>
</ul>
<h2 id="7-which-usually-has-a-higher-miss-rate-i-cache-or-d-cache">7. Which Usually Has a Higher Miss Rate: I-cache or D-cache?</h2>
<p>Usually:</p>
<p>$\text{D-cache miss rate} &gt; \text{I-cache miss rate}$</p>
<p>Instruction accesses often have strong spatial and temporal locality.
For example, instructions are frequently executed sequentially:</p>
<ul>
<li>instruction 100</li>
<li>instruction 101</li>
<li>instruction 102</li>
<li>instruction 103
Loops also repeatedly execute the same instructions.
Data accesses can be much more irregular.
Examples:</li>
<li>pointer chasing</li>
<li>linked lists</li>
<li>hash tables</li>
<li>random array accesses
So D-cache often has a higher miss rate.
This is workload-dependent, though.</li>
</ul>
<h2 id="8-write-through-vs-write-back">8. Write-Through vs Write-Back</h2>
<p>This mainly matters for data caches.
Write-Through
When the CPU writes data, both the cache and the next memory level are updated immediately.
Advantages:</p>
<ul>
<li>simpler consistency</li>
<li>memory is always up to date
Disadvantages:</li>
<li>more memory traffic</li>
<li>potentially slower writes
Write-Back
The CPU writes only to the cache first.
The cache line is marked dirty.
When that line is later evicted, the modified data is written to the next memory level.
Advantages:</li>
<li>less lower-level memory traffic</li>
<li>usually better performance
Disadvantages:</li>
<li>more complex</li>
<li>requires dirty-bit management
Modern high-performance data caches commonly use write-back.</li>
</ul>
<h2 id="9-replacement-policy">9. Replacement Policy</h2>
<p>In a set-associative cache, a set may become full.
Suppose a 4-way set already contains four blocks.
If a new block must enter that set, one old block must be removed.
The replacement policy decides which one.
Common policies include:</p>
<ul>
<li>LRU</li>
<li>Pseudo-LRU</li>
<li>FIFO</li>
<li>Random
L1 often prefers simpler policies because access speed is critical.
L2 may use more sophisticated policies because reducing misses is more important.</li>
</ul>
<h2 id="10-inclusive-cache">10. Inclusive Cache</h2>
<p>In an inclusive hierarchy, every block in L1 must also exist in L2.
Conceptually:</p>
<p>$L1 \subseteq L2$</p>
<pre><code>Example:
L1 contains:
- A
- B
- C
L2 contains:
- A
- B
- C
- D
- E</code></pre><p>Advantages:</p>
<ul>
<li>can simplify some coherence and tracking mechanisms
Disadvantages:</li>
<li>duplicate copies use cache capacity</li>
</ul>
<h2 id="11-exclusive-cache">11. Exclusive Cache</h2>
<p>In an exclusive hierarchy, the same block is generally kept in only one cache level at a time.</p>
<pre><code>Example:
L1 contains:
- A
- B
- C
L2 contains:
- D
- E
- F
- G</code></pre><p>Advantage:</p>
<ul>
<li>better effective total cache capacity
Disadvantage:</li>
<li>more complicated movement of blocks between levels</li>
</ul>
<h2 id="12-non-inclusive--non-exclusive">12. Non-Inclusive / Non-Exclusive</h2>
<p>Some processors use neither a strictly inclusive nor strictly exclusive policy.
A block may exist in multiple cache levels, but it is not required to.
This gives the cache hierarchy more flexibility.</p>
<h2 id="13-l3-cache">13. L3 Cache</h2>
<p>Modern processors often also include L3 cache.
The hierarchy becomes:
CPU → L1 → L2 → L3 → DRAM
Typical trend:</p>
<ul>
<li>L1: smallest and fastest</li>
<li>L2: larger and slower</li>
<li>L3: much larger and slower</li>
<li>DRAM: largest but much slower
The main purpose of L3 is to reduce expensive DRAM accesses.
In multicore processors, L3 is often shared by multiple cores.</li>
</ul>
<h2 id="l1-vs-l2-summary">L1 vs L2 Summary</h2>
<table>
<thead>
<tr>
<th>Feature</th>
<th>L1</th>
<th>L2</th>
</tr>
</thead>
<tbody><tr>
<td>Size</td>
<td>Small</td>
<td>Larger</td>
</tr>
<tr>
<td>Speed</td>
<td>Fastest</td>
<td>Slower</td>
</tr>
<tr>
<td>Main goal</td>
<td>Low hit time</td>
<td>Lower miss rate and larger capacity</td>
</tr>
<tr>
<td>Associativity</td>
<td>Usually lower</td>
<td>Usually higher</td>
</tr>
<tr>
<td>Split / Unified</td>
<td>Often split</td>
<td>Usually unified</td>
</tr>
<tr>
<td>Ports / bandwidth</td>
<td>Very important</td>
<td>Less latency-critical than L1</td>
</tr>
<tr>
<td>Replacement policy</td>
<td>Usually simpler</td>
<td>Can be more sophisticated</td>
</tr>
<tr>
<td>Write policy</td>
<td>D-cache often write-back</td>
<td>Often write-back</td>
</tr>
<tr>
<td>Miss rate</td>
<td>Usually higher than L2</td>
<td>Lower than L1</td>
</tr>
<tr>
<td>Latency</td>
<td>Very low</td>
<td>Higher</td>
</tr>
</tbody></table>
<p>The main design principle is:</p>
<p>$\text{L1: optimize for speed}$</p>
<p>$\text{L2/L3: optimize more for capacity and miss-rate reduction}$</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Multi-Level Cache ]]></title>
            <link>https://velog.io/@houston_guy2/Multi-Level-Cache</link>
            <guid>https://velog.io/@houston_guy2/Multi-Level-Cache</guid>
            <pubDate>Mon, 28 Sep 2026 15:40:00 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylecolororange--reducing-miss-penalty-span"><span style="color:orange">  Reducing Miss Penalty </span></h1>
<ol>
<li>Multi-Level Caches</li>
<li>Victim Caches</li>
</ol>
<h2 id="1-multi-level-caches">1. Multi-level Caches</h2>
<p>There is a trade-off between the size of the cache and the access time</p>
<ul>
<li><p>Small caches are faster but cannot hold much data, therefore having larger miss rate</p>
</li>
<li><p>Large caches can hold enough data but are slower, thus increasing hit time</p>
</li>
<li><p>We can add another, smaller cache (L1) between the current cache (L2) and the CPU</p>
</li>
<li><p>Since everything that is in L1 is also in L2, it only makes sense to add L2 if it is much bigger than L1</p>
</li>
</ul>
<p>The CPU checks L1 first. If L1 misses, it checks L2. If L2 also misses, it accesses main memory.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f2758771-684e-463d-ae2d-ff22f95a9f6a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/682a3b4d-5c01-40ad-9587-f8fa46494dd2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/b6c7b456-5114-4144-8bde-cb7657a230c3/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/8abe78bc-e620-496e-980d-ecd9a61a1007/image.png" alt=""></p>
<p>We now have local and global miss rate</p>
<ul>
<li><p>Local miss rate is number of misses in the given cache level divided by number of accesses to this level and it is equal to Miss_RateL1 for L1 and Miss_RateL2 for L2</p>
</li>
<li><p>Global miss rate is the number of misses in the given cache level divided by total number of accesses generated by the CPU. It is equal to Miss_RateL1 for L1, but it is Miss_RateL1*Miss_RateL2 for L2</p>
</li>
</ul>
<h1 id="average-memory-stallsinstruction">Average Memory Stalls/Instruction</h1>
<p>$\boxed{\frac{Average Memory Stalls}{Instruction}=(\text{Misses/Inst}<em>{L1})(HT</em>{L2}) + (\text{Misses/Inst}<em>{L2})(MP</em>{L2})}$</p>
<hr>
<h3 id="example-1">Example 1</h3>
<p>Assume that in 1000 memory references there are 40 misses in L1 cache and 20 misses in L2 cache. Assume the miss penalty for L2 is 200 clock cycles, hit time of L2 is 10 clock cycles, hit time of L1 is 1 clock cycle and there are 1.5 memory references per instruction. What are the average memory access time and the average memory stalls per instruction?</p>
<h3 id="example-2">Example 2</h3>
<p>Given the data below, what is the impact of second-level cache associativity on its miss
penalty:</p>
<ul>
<li>Hit time for direct mapped L2 is 10 clock cycles</li>
<li>Two-way associativity increases hit time by 0.1 clock cycles to 10.1 clock cycles</li>
<li>Local miss rate in L2 for direct mapped is 25%</li>
<li>Local miss rate in L2 for two-way set associative is 20%</li>
<li>Miss penalty for L2 is 200 clock cycles</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[HW2 ]]></title>
            <link>https://velog.io/@houston_guy2/HW2</link>
            <guid>https://velog.io/@houston_guy2/HW2</guid>
            <pubDate>Sat, 26 Sep 2026 22:10:16 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/houston_guy2/post/f3f9c509-747c-4c9b-bbc6-4499fe00639c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/ccc5f3fa-142c-47a3-94ce-a87479678007/image.png" alt=""></p>
<p>Block: 1 cache line stores 1 block
Cache line = one cell
Block = the data stored inside that cell
Block size = how many bytes that cell stores</p>
<h1 id="1">1.</h1>
<p>Assume we are using 32-bit address. L1 cache is direct mapped and size is 16KB, block size is 32B. L2 cache is 8-way set associative and size is 4MB, block size is 64B. Draw the cache access diagram for L1 and L2 cache respectively, and indicate the size of all fields (in bits) and the size of L1 and L2 caches (in number of blocks they can hold). You may use the Lec5-slide8 as a reference.</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/424b59e6-f11a-41ae-8889-6e6d5208d1f9/image.png" alt=""></p>
<h1 id="2">2.</h1>
<p>You have split caches (a 32KB instruction cache and a 32KB data cache) with the following specifications:</p>
<ul>
<li>Hit time is 1 clock cycle and miss penalty is 100 cycles</li>
<li>There are 38.4 misses per 1000 instructions for 32KB data cache, and 1.36 misses per 1000 instructions for 32KB instruction cache.</li>
</ul>
<p>Assume 40% of instructions are data transfer instructions, CPI is 1 when all memory accesses hit in the cache.</p>
<h3 id="a-what-is-the-overall-average-memory-access-time-for-the-split-caches">(a) What is the overall average memory access time for the split caches?</h3>
<h3 id="b-what-is-the-overall-average-memory-stalls-per-instruction-for-the-split-caches">(b) What is the overall average memory stalls per instruction for the split caches?</h3>
<h3 id="c-what-is-the-overall-cputime-expressed-as-function-of-ic---instruction-count">(c) What is the overall CPUtime (expressed as function of IC - instruction count)?</h3>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/d170a0f4-e993-4d48-93f5-01303039dc6b/image.png" alt=""></p>
<h1 id="3">3.</h1>
<p>You have a two-level cache with the following specifications:</p>
<ul>
<li>L1 is 64KB direct-mapped with 32B blocks. Hit time is 1ns and local miss rate L1 is 5%</li>
<li>L2 is 1MB 4-way set-associative with 128B blocks. Hit time is 20ns and global miss rateL2 is 2%</li>
<li>Penalty to fetch block from memory to L2 is 60ns</li>
</ul>
<p>What is the average memory access time?</p>
<p><img src="https://velog.velcdn.com/images/houston_guy2/post/aee04be6-0a4e-4cd2-8ea7-ef35ba5adce1/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>