0. 학습 목표
- 지역성(Locality)이 왜 메모리 계층구조를 가능하게 하는지 설명한다.
- 캐시의 핵심 설계 요소(배치/탐색/교체/쓰기 정책)를 정의하고, 성능 지표(AMAT, CPI 영향)를 계산한다.
- 연관도, 블록 크기, 다단 캐시, TLB/가상메모리, 일관성(coherence) 문제를 큰 그림으로 연결한다.
1. 지역성의 원리(Principle of Locality)
1.1. 프로그램은 항상 전체 메모리를 쓰지 않는다
- 프로그램은 어느 시점이든 주소공간의 일부만 집중적으로 접근한다.
1.2. 두 종류의 지역성
-
시간적 지역성(Temporal locality): 최근 접근한 항목은 곧 다시 접근될 가능성이 높다.
- 예: 루프의 명령어, 반복적으로 갱신되는 변수(Induction variable)
-
공간적 지역성(Spatial locality): 최근 접근한 위치 근처도 곧 접근될 가능성이 높다.
- 예: 순차적 명령어 인출, 배열 데이터 순회
2. 지역성을 계층구조로 활용하기
2.1. 메모리 계층 구조의 직관
- 느리지만 큰 저장장치(디스크)에 모든 것을 두고,
- 최근(그리고 근처) 데이터를 더 작은 DRAM(메인 메모리)로 복사,
- 더 최근(그리고 근처) 데이터를 SRAM(캐시, CPU 근접)으로 복사한다.
2.2. 계층에서의 기본 용어
- 블록(block/line): 계층 간 복사의 최소 단위(보통 여러 워드 포함)
- 상위 계층에 있으면 히트(hit), 없으면 미스(miss)
- 히트율(hit ratio) = hits/accesses
- 미스율(miss ratio) = misses/accesses = 1 − hit ratio
- 미스 패널티(miss penalty): 하위 계층에서 블록을 가져오는데 드는 시간
3. 메모리 기술(Technology) 개요
3.1. SRAM vs DRAM vs 디스크 (속도·가격 스케일)
- SRAM: 0.5–2.5ns, 매우 비쌈(GB당 수백~천 달러)
- DRAM: 50–70ns, 상대적으로 저렴
- 디스크: 5–20ms, 매우 저렴(GB당 센트 단위)
- 이상적 메모리는 SRAM의 속도 + 디스크의 용량/가격을 동시에 갖는 것(현실적으로 불가)
3.2. DRAM 동작 핵심
- 캐패시터에 전하로 저장, 트랜지스터 1개로 접근
- 주기적 리프레시(refresh) 필요(행(row) 단위로 읽고 다시 씀)
3.3. DRAM 고급 조직
- 비트는 2D 배열(행/열)
- DRAM은 보통 행 전체(row) 활성화
- 버스트 모드(burst mode): 한 행에서 연속 워드를 더 낮은 지연으로 공급
- DDR: 상승/하강 에지 모두 전송, QDR: DDR 입출력 분리
3.4. DRAM 세대(용량 증가, $/GB 하락)
- 1980년대 Kibibit → 2010s Gibibit로 급격한 집적도 증가 및 가격 변화
3.5. DRAM 성능 요인
- Row buffer(병렬 read/refresh), Synchronous DRAM(연속 burst), Banking(병렬 접근) → 주로 대역폭 개선
4. 플래시와 디스크(저장장치 관점)
4.1. 플래시(Flash)
- 비휘발성, 디스크보다 100~1000배 빠름, 작고 저전력·견고
- 단, $/GB는 디스크보다 비싸고 DRAM보다는 쌈(중간 지대)
4.2. 플래시 타입
- NOR: 랜덤 읽기/쓰기, 임베디드 instruction memory
- NAND: 더 고밀도, 블록 단위 접근, GB당 저렴(USB/스토리지)
- 마모(wear-out) 문제 → wear leveling 필요
4.3. 디스크 접근 시간 구성요소
- 섹터: ID + 데이터(512B, 4096B 제안) + ECC + 동기화 필드/갭
- 접근 비용: 큐잉 지연 + 탐색(seek) + 회전 지연 + 전송 + 컨트롤러 오버헤드
- 예제(15000rpm 등)에서 평균 읽기 시간이 6.2ms로 계산됨
- 실제 시스템은 지역성과 스케줄링으로 평균 seek가 더 작아질 수 있으며, 디스크 내부 캐시/프리패치도 활용
5. 캐시의 기본(Basics of Caches)
5.1. 캐시는 CPU에 가장 가까운 계층
- 캐시 설계의 첫 질문 두 가지
- 데이터가 캐시에 있는지 어떻게 아는가?
- 어디를 찾아봐야 하는가?
5.2. Direct-mapped cache
- 각 메모리 블록은 캐시의 딱 1곳에만 들어갈 수 있음
- 매핑: (블록 주소) mod (캐시 블록 수)
- 블록 수는 2의 거듭제곱 → 하위 비트로 index 구성
5.3. Tag와 Valid bit
- 같은 index로 여러 메모리 블록이 충돌할 수 있으므로, 어떤 블록이 들어있는지 식별 필요
- 블록 주소의 상위 비트를 tag로 저장
- 해당 라인이 비어있는지 확인하는 valid bit(초기 0)
5.4. 예제로 보는 direct-mapped 동작 흐름
- 8블록 캐시, 1 word/block 예시에서
- 첫 접근은 miss → 해당 index에 (tag, data) 채움
- 이후 같은 블록 재접근은 hit
- 같은 index에 다른 tag가 들어오면 기존 내용이 교체됨(충돌)
5.5. 주소 분해(Address subdivision)
- 주소는 일반적으로 Tag | Index | Offset으로 분해
- offset은 블록 내부 바이트/워드 위치 결정
- 슬라이드 도식(페이지 25)에서 tag 비교 후 hit 여부 결정 흐름 제시
6. 블록 크기(Block size) 트레이드오프
6.1. 큰 블록은 언제 좋은가?
- 공간적 지역성 덕분에 큰 블록은 보통 미스율 감소에 유리
6.2. 하지만 캐시가 고정 크기라면
- 블록이 커지면 블록 개수 감소 → 경쟁 증가 → 미스율이 오히려 증가 가능
- 큰 블록은 pollution(불필요 데이터 유입) 증가
- 그리고 미스 시 한 번에 가져올 데이터가 많아져 미스 패널티 증가
- 완화 기법: early restart, critical-word-first
6.3. 계산 예시(블록 매핑)
- 64블록, 16B/block에서 주소 1200은
- 블록 주소 ⌊1200/16⌋ = 75
- 캐시 블록 번호 = 75 mod 64 = 11
- Tag/Index/Offset 비트수도 함께 제시됨
7. 미스가 발생하면 CPU는?
- hit: 정상 진행
- miss: 파이프라인 stall + 하위 계층에서 블록 fetch
- I-cache miss: 인스트럭션 fetch 재시작
- D-cache miss: 데이터 접근 완료까지 대기
8. 쓰기 정책(Write policy)
8.1. Write-through
- 캐시 히트에서 캐시만 갱신하면 메모리와 불일치
- write-through: 캐시 + 메모리 둘 다 갱신
- 문제: 메모리 쓰기가 느리면 CPI가 폭증 가능
- 해법: write buffer(CPU는 즉시 진행, 버퍼가 찼을 때만 stall)
8.2. Write-back
- 히트 시 캐시만 갱신, 블록이 교체될 때 메모리에 반영
- 블록이 수정되었는지 dirty bit로 추적
- 교체 중 write-back도 write buffer로 숨길 수 있음
8.3. Write miss 정책(Write allocation)
-
write-through에서 선택지
- write allocate: 미스 시 블록을 가져와 캐시에 올린 뒤 씀
- write around: 블록을 안 가져오고 하위 계층에 바로 씀
-
이유: 초기화처럼 한 블록을 쓴 뒤 읽는 패턴이면 write around가 유리할 수 있음
-
write-back은 보통 블록을 가져오는 쪽(allocate)을 사용
9. 캐시 성능 측정: CPI/AMAT
9.1. CPU 시간 구성
-
실행 사이클(히트 시간 포함) + 메모리 스톨 사이클(주로 미스)
-
단순화하면
- Memory stall cycles ≈ (Memory accesses / Program) × Miss rate × Miss penalty
9.2. CPI 예제
- I-miss=2%, D-miss=4%, miss penalty=100 cycles, base CPI=2, load/store=36%
- I쪽 미스 비용: 0.02×100 = 2 cycles/inst
- D쪽 미스 비용: 0.36×0.04×100 = 1.44 cycles/inst
- 실제 CPI = 2 + 2 + 1.44 = 5.44
9.3. AMAT(Average Memory Access Time)
- AMAT = Hit time + Miss rate × Miss penalty
- 예: hit 1cycle, miss penalty 20cycles, miss rate 5% → AMAT = 1 + 0.05×20 = 2 cycles(=2ns)
9.4. 핵심 메시지
- CPU가 빨라질수록(클럭↑, base CPI↓) 미스 패널티의 상대적 영향이 더 커짐
- 따라서 시스템 평가에서 캐시/메모리 거동을 무시하면 안 됨
10. 연관도(Associativity): direct ↔ set associative ↔ fully associative
10.1. 정의와 비용
- Fully associative: 아무 엔트리나 배치 가능 → 모든 엔트리를 동시에 비교(비용 큼)
- n-way set associative: set을 먼저 정하고(set index), 그 set 안에서 n개를 병렬 비교(n개의 comparator)
10.2. 예제로 보는 효과(미스 감소, 수익 체감)
-
64KB D-cache, 16-word block, SPEC2000에서
- 1-way 10.3% → 2-way 8.6% → 4-way 8.3% → 8-way 8.1%
-
연관도를 늘리면 미스는 줄지만 효과는 점점 작아짐
10.3. 교체 정책(Replacement policy)
- direct-mapped: 선택권 없음
- set associative: (1) invalid 엔트리 우선, (2) 아니면 교체 정책 적용
- LRU: 2-way는 간단, 4-way는 관리 가능, 그 이상은 복잡
- Random: 높은 연관도에서 LRU와 성능이 거의 비슷하며 구현 쉬움
11. 다단 캐시(Multilevel caches)
11.1. L1/L2/L3 구조
- L1(Primary): 작고 빠름, hit time 최소화
- L2: 더 크고 느리지만 DRAM보다 빠름, 미스율 최소화(특히 main memory로 가는 global miss rate)
- 고급 시스템은 L3도 포함
11.2. CPI 예시(왜 L2가 필요한가)
- base CPI=1, 4GHz(0.25ns/cycle), miss rate=2%, main memory=100ns
- L1만 있으면 miss penalty=100ns/0.25ns=400 cycles
- CPI = 1 + 0.02×400 = 9
- L2 추가: L2 access=5ns(=20 cycles), global miss=0.5%
- CPI = 1 + 0.02×20 + 0.005×400 = 3.4 → 9/3.4=2.6배 성능 개선
12. 고급 CPU/소프트웨어와의 상호작용
12.1. Out-of-order CPU
- 미스 동안에도 독립 명령어를 실행할 수 있어 미스의 영향이 단순 CPI/AMAT만으로는 분석 어려움
- 데이터 의존성에 좌우 → 보통 시뮬레이션 기반 평가
12.2. 소프트웨어 접근 패턴
- 미스는 알고리즘/컴파일러 최적화에 의해 크게 달라짐
12.3. Blocking 최적화(DGEMM)
- 목표: 데이터가 캐시에서 교체되기 전에 재사용을 최대화
- DGEMM 기본 루프(내부 루프)와, BLOCKSIZE 기반 cache blocking 코드
- 블로킹 전/후로 지역성이 개선되는 흐름