메모리 계층 구조
메모리 계층 구조¶
개요¶
컴퓨터 시스템에서 CPU와 메모리 간의 속도 차이는 성능의 주요 병목입니다. 메모리 계층 구조(Memory Hierarchy)는 속도, 용량, 비용의 트레이드오프를 최적화하여 이 문제를 해결합니다. 이 레슨에서는 메모리 계층 구조의 원리, 지역성의 개념, 그리고 각 계층의 특성을 학습합니다.
난이도: ⭐⭐
선수 지식: 컴퓨터 시스템 개요, CPU 구조 기초
목차¶
1. 메모리 계층 구조의 필요성¶
1.1 CPU-메모리 속도 격차¶
CPU와 메모리 성능 발전 추이 (1980-2020):
성능
│
│ ●────────── CPU
│ ●
│ ●
│ ●
│ ●
│ ●
│ ● ◆───────── 메모리
│ ● ◆
│ ● ◆
│ ◆
│ ◆
│ ◆
│ ◆
│ ◆
└─────────────────────────────────────────────────── 연도
1980 1990 2000 2010 2020
CPU: 연간 ~50% 성능 향상 (1980-2000)
메모리: 연간 ~7% 성능 향상
결과: "Memory Wall" - 메모리가 시스템 성능의 병목
1.2 이상적인 메모리 vs 현실¶
이상적인 메모리:
┌─────────────────────────────────────────┐
│ - 무한대 용량 │
│ - 0 접근 시간 │
│ - 무료 │
└─────────────────────────────────────────┘
현실:
┌─────────────────────────────────────────────────────────────┐
│ 속도 ↑ = 비용 ↑, 용량 ↓ │
│ 용량 ↑ = 비용 ↑, 속도 ↓ │
│ 비용 ↓ = 속도 ↓, 용량 제한 │
└─────────────────────────────────────────────────────────────┘
해결책: 메모리 계층 구조
- 여러 종류의 메모리를 계층적으로 배치
- 빠르고 작은 메모리를 CPU 가까이
- 느리고 큰 메모리를 멀리
- 자주 사용하는 데이터를 빠른 메모리에 유지
1.3 메모리 계층 개념¶
┌───────────┐
│ 레지스터 │ ← 가장 빠름, 가장 작음, 가장 비쌈
│ ~1KB │
└─────┬─────┘
│
┌─────┴─────┐
│ L1 캐시 │
│ ~64KB │
└─────┬─────┘
│
┌─────────┴─────────┐
│ L2 캐시 │
│ ~256KB-1MB │
└─────────┬─────────┘
│
┌─────────────────┴─────────────────┐
│ L3 캐시 │
│ ~2-32MB │
└─────────────────┬─────────────────┘
│
┌─────────────────────────┴─────────────────────────┐
│ 메인 메모리 │
│ ~8-128GB │
└─────────────────────────┬─────────────────────────┘
│
┌─────────────────────────────┴─────────────────────────────┐
│ SSD/HDD │
│ ~256GB-10TB │ ← 가장 느림, 가장 큼, 가장 저렴
└───────────────────────────────────────────────────────────┘
위로 갈수록: 빠름, 작음, 비쌈, CPU에 가까움
아래로 갈수록: 느림, 큼, 저렴, CPU에서 멀음
2. 지역성 원리¶
2.1 지역성이란?¶
프로그램이 메모리를 접근하는 패턴에는 규칙성이 있습니다. 이를 지역성(Locality)이라 하며, 메모리 계층 구조가 효과적인 이유입니다.
2.2 시간적 지역성 (Temporal Locality)¶
정의: 최근에 접근한 데이터는 가까운 미래에 다시 접근될 가능성이 높음
예시 1: 반복문의 변수
┌─────────────────────────────────────┐
│ for (i = 0; i < 1000; i++) { │
│ sum += array[i]; │
│ } │
└─────────────────────────────────────┘
변수 'sum', 'i'는 1000번 반복 접근
예시 2: 자주 호출되는 함수
┌─────────────────────────────────────┐
│ while (running) { │
│ process_event(); // 반복 호출 │
│ update_state(); // 반복 호출 │
│ } │
└─────────────────────────────────────┘
함수 코드는 계속 재사용됨
시간적 지역성 활용:
- 최근 접근한 데이터를 캐시에 유지
- 다시 접근 시 캐시에서 빠르게 제공
2.3 공간적 지역성 (Spatial Locality)¶
정의: 접근한 데이터 주변의 데이터도 곧 접근될 가능성이 높음
예시 1: 배열 순차 접근
┌─────────────────────────────────────┐
│ for (i = 0; i < N; i++) { │
│ sum += array[i]; │
│ } │
└─────────────────────────────────────┘
메모리 레이아웃:
주소: 0x100 0x104 0x108 0x10C 0x110 0x114
┌──────┬──────┬──────┬──────┬──────┬──────┐
array: │ a[0] │ a[1] │ a[2] │ a[3] │ a[4] │ a[5] │
└──────┴──────┴──────┴──────┴──────┴──────┘
↓ ↓ ↓ ↓ ↓ ↓
순차적으로 접근 - 공간적 지역성 높음
예시 2: 구조체 접근
┌─────────────────────────────────────┐
│ struct Point { int x, y, z; }; │
│ Point p; │
│ distance = sqrt(p.x*p.x + │
│ p.y*p.y + │
│ p.z*p.z); │
└─────────────────────────────────────┘
p.x, p.y, p.z는 메모리상 인접
공간적 지역성 활용:
- 캐시 라인(블록) 단위로 데이터 로드
- 한 번에 여러 인접 데이터 가져옴
- 64바이트 캐시 라인 = 16개 int 포함
2.4 지역성을 활용한 최적화¶
좋은 코드 (높은 지역성):
// 행 우선 순회 - 메모리 레이아웃에 맞춤
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++) {
sum += matrix[i][j];
}
}
메모리 접근 패턴:
[0,0] [0,1] [0,2] [0,3] ... [1,0] [1,1] ...
↓ ↓ ↓ ↓ ↓ ↓
순차적 접근 → 캐시 히트율 높음
나쁜 코드 (낮은 지역성):
// 열 우선 순회 - 메모리 레이아웃과 불일치
for (j = 0; j < N; j++) {
for (i = 0; i < N; i++) {
sum += matrix[i][j];
}
}
메모리 접근 패턴:
[0,0] [1,0] [2,0] [3,0] ... [0,1] [1,1] ...
↓ ↓ ↓ ↓ ↓ ↓
N개 간격 점프 → 캐시 미스 빈번
성능 차이 (N=1024, 8바이트 원소):
- 행 우선: ~50ms
- 열 우선: ~500ms (10배 느림)
2.5 Working Set¶
정의: 특정 시간 구간 동안 프로그램이 활발히 사용하는 메모리 영역
┌─────────────────────────────────────────────────────────────┐
│ │
│ 프로그램 실행 단계별 Working Set 변화 │
│ │
│ 단계 1: 초기화 │
│ Working Set: [초기화 코드] + [설정 데이터] │
│ 크기: ~100KB │
│ │
│ 단계 2: 데이터 처리 │
│ Working Set: [처리 코드] + [입력 버퍼] + [출력 버퍼] │
│ 크기: ~10MB │
│ │
│ 단계 3: 결과 출력 │
│ Working Set: [출력 코드] + [출력 버퍼] │
│ 크기: ~500KB │
│ │
└─────────────────────────────────────────────────────────────┘
캐시 크기와 Working Set:
- Working Set < 캐시 크기: 높은 히트율
- Working Set > 캐시 크기: 스래싱 발생 가능
3. 메모리 기술 비교¶
3.1 SRAM (Static RAM)¶
SRAM 셀 구조 (6T SRAM):
Vdd Vdd
│ │
┌──┴──┐ ┌──┴──┐
│ P1 │ │ P2 │
└──┬──┘ └──┬──┘
│ ┌───┐ │
├──────────┤ ├────────────┤
│ └───┘ │
┌──┴──┐ ┌──┴──┐
│ N1 │ Word │ N2 │
└──┬──┘ Line └──┬──┘
│ │ │
│ ┌──┴──┐ │
│ │ N3 │ │
│ └──┬──┘ │
│ │ │
Bit GND ~Bit
Line Line
특징:
- 6개 트랜지스터로 1비트 저장
- 전원 공급 시 데이터 유지 (리프레시 불필요)
- 빠른 접근 속도 (~1-2ns)
- 높은 비용, 낮은 집적도
- 용도: 캐시 메모리, 레지스터 파일
3.2 DRAM (Dynamic RAM)¶
DRAM 셀 구조 (1T1C DRAM):
Word Line
│
┌──┴──┐
│ T │ (트랜지스터)
└──┬──┘
│
┌──┴──┐
│ C │ (커패시터)
└──┬──┘
│
Bit Line
특징:
- 1개 트랜지스터 + 1개 커패시터로 1비트 저장
- 커패시터 누수로 인해 주기적 리프레시 필요 (~64ms)
- 느린 접근 속도 (~50-100ns)
- 저렴한 비용, 높은 집적도
- 용도: 메인 메모리 (DDR4, DDR5)
DRAM 접근 과정:
1. Row Activate (RAS): 행 선택, 센스 앰프로 데이터 이동
2. Column Read (CAS): 열 선택, 데이터 출력
3. Precharge: 다음 접근 준비
┌─────────────────────────────────────────────────────┐
│ RAS Latency │ CAS Latency │ Precharge │ = 전체 지연 │
└─────────────────────────────────────────────────────┘
~15ns ~15ns ~15ns ~45ns+
3.3 플래시 메모리 (SSD)¶
NAND Flash 구조:
Word Line 0 Word Line 1 Word Line 2
│ │ │
┌─────┼───────────┼───────────┼─────┐
│ ┌──┴──┐ ┌──┴──┐ ┌──┴──┐ │
│ │Cell │ │Cell │ │Cell │ │ String 0
│ └──┬──┘ └──┬──┘ └──┬──┘ │
│ │ │ │ │
│ ┌──┴──┐ ┌──┴──┐ ┌──┴──┐ │
│ │Cell │ │Cell │ │Cell │ │ String 1
│ └──┬──┘ └──┬──┘ └──┬──┘ │
└─────┼───────────┼───────────┼─────┘
│ │ │
Bit Line Bit Line Bit Line
특징:
- 플로팅 게이트에 전하 저장
- 비휘발성 (전원 없어도 데이터 유지)
- 읽기: ~25us, 쓰기: ~250us, 삭제: ~2ms
- 블록 단위 삭제만 가능
- 용도: SSD, USB 드라이브, SD 카드
3.4 메모리 기술 비교표¶
┌──────────────┬───────────┬───────────┬───────────┬───────────┐
│ 특성 │ SRAM │ DRAM │ NAND │ HDD │
├──────────────┼───────────┼───────────┼───────────┼───────────┤
│ 접근 시간 │ 1-2ns │ 50-100ns │ 25us │ 5-10ms │
├──────────────┼───────────┼───────────┼───────────┼───────────┤
│ 비용/GB │ ~$5000 │ ~$3-5 │ ~$0.1 │ ~$0.02 │
├──────────────┼───────────┼───────────┼───────────┼───────────┤
│ 휘발성 │ Yes │ Yes │ No │ No │
├──────────────┼───────────┼───────────┼───────────┼───────────┤
│ 일반 용량 │ ~32MB │ ~128GB │ ~4TB │ ~20TB │
├──────────────┼───────────┼───────────┼───────────┼───────────┤
│ 밀도 │ 낮음 │ 높음 │ 매우 높음 │ 매우 높음 │
├──────────────┼───────────┼───────────┼───────────┼───────────┤
│ 전력 소비 │ 높음 │ 중간 │ 낮음 │ 높음 │
├──────────────┼───────────┼───────────┼───────────┼───────────┤
│ 주 용도 │ 캐시 │ 메인 메모리│ SSD │ 대용량 저장│
└──────────────┴───────────┴───────────┴───────────┴───────────┘
4. 메모리 계층¶
4.1 레지스터¶
위치: CPU 내부
용량: 수십 ~ 수백 개 (x86-64: 16개 범용 + 기타)
속도: 1 사이클 (0.3ns @ 3GHz)
레지스터 종류:
┌────────────────────────────────────────────────────────┐
│ 범용 레지스터 │ RAX, RBX, RCX, RDX, RSI, RDI, ... │
├────────────────────────────────────────────────────────┤
│ 프로그램 카운터 │ RIP (Instruction Pointer) │
├────────────────────────────────────────────────────────┤
│ 스택 포인터 │ RSP, RBP │
├────────────────────────────────────────────────────────┤
│ 플래그 레지스터 │ RFLAGS (Zero, Carry, Sign, ...) │
├────────────────────────────────────────────────────────┤
│ 벡터 레지스터 │ XMM0-15, YMM0-15, ZMM0-31 (SIMD) │
├────────────────────────────────────────────────────────┤
│ 부동소수점 │ ST0-ST7 (x87), XMM (SSE) │
└────────────────────────────────────────────────────────┘
레지스터 vs 메모리 성능:
- 레지스터 연산: ADD R1, R2, R3 → 1 사이클
- 메모리 연산: ADD R1, [mem] → 4+ 사이클 (L1 히트)
4.2 캐시 메모리¶
캐시 계층 (현대 CPU):
┌─────────────────────────────────────────────────────────────┐
│ Core 0 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ L1 I-Cache │ │ L1 D-Cache │ │ │
│ │ │ 32KB │ │ 32KB │ │ │
│ │ │ 4 cycles │ │ 4 cycles │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ │ │
│ │ │ │ │ │
│ │ └─────────┬─────────┘ │ │
│ │ │ │ │
│ │ ┌─────────┴─────────┐ │ │
│ │ │ L2 Cache │ │ │
│ │ │ 256KB │ │ │
│ │ │ 12 cycles │ │ │
│ │ └─────────┬─────────┘ │ │
│ └───────────────────┼──────────────────────────────────┘ │
│ │ │
├──────────────────────┼───────────────────────────────────────┤
│ │ Core 1 │
│ │ (동일 구조) │
├──────────────────────┼───────────────────────────────────────┤
│ ┌──────────┴──────────┐ │
│ │ L3 Cache │ │
│ │ 8-32MB (공유) │ │
│ │ 40 cycles │ │
│ └──────────┬──────────┘ │
└──────────────────────┼───────────────────────────────────────┘
│
▼
┌─────────────────┐
│ Main Memory │
│ 100+ cycles │
└─────────────────┘
캐시 특성 요약:
┌───────┬──────────┬───────────────┬──────────────────────────┐
│ 레벨 │ 용량 │ 지연시간 │ 특징 │
├───────┼──────────┼───────────────┼──────────────────────────┤
│ L1 │ 32-64KB │ 4 cycles │ 코어 전용, I/D 분리 │
├───────┼──────────┼───────────────┼──────────────────────────┤
│ L2 │ 256KB-1MB│ 12 cycles │ 코어 전용 또는 공유 │
├───────┼──────────┼───────────────┼──────────────────────────┤
│ L3 │ 8-64MB │ 40 cycles │ 전체 코어 공유 │
└───────┴──────────┴───────────────┴──────────────────────────┘
4.3 메인 메모리 (DRAM)¶
현대 메모리 시스템 구조:
┌─────────────────────────────────────────────┐
│ Memory Controller │
│ (CPU 또는 노스브리지 내장) │
└──────────────────┬──────────────────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│Channel 0│ │Channel 1│ │Channel 2│
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│ DIMM 0 │ │ DIMM 0 │ │ DIMM 0 │
│ 16GB │ │ 16GB │ │ 16GB │
└─────────┘ └─────────┘ └─────────┘
DDR (Double Data Rate) 발전:
┌────────┬─────────────┬──────────────┬───────────────────┐
│ 세대 │ 클럭 속도 │ 대역폭/채널 │ 특징 │
├────────┼─────────────┼──────────────┼───────────────────┤
│ DDR3 │ 800-2133 │ 6.4-17GB/s │ 1.5V │
├────────┼─────────────┼──────────────┼───────────────────┤
│ DDR4 │ 1600-3200 │ 12.8-25.6GB/s│ 1.2V, 뱅크 그룹 │
├────────┼─────────────┼──────────────┼───────────────────┤
│ DDR5 │ 3200-6400+ │ 25.6-51.2GB/s│ 1.1V, 내장 ECC │
└────────┴─────────────┴──────────────┴───────────────────┘
4.4 보조 기억장치¶
SSD 구조:
┌─────────────────────────────────────────────────────────────┐
│ SSD Controller │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Flash Translation Layer (FTL) │ │
│ │ - 논리 주소 → 물리 주소 변환 │ │
│ │ - Wear Leveling │ │
│ │ - Garbage Collection │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ NAND │ │ NAND │ │ NAND │ │ NAND │ │
│ │ Package │ │ Package │ │ Package │ │ Package │ │
│ │ 0 │ │ 1 │ │ 2 │ │ 3 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ NAND │ │ NAND │ │ NAND │ │ NAND │ │
│ │ Package │ │ Package │ │ Package │ │ Package │ │
│ │ 4 │ │ 5 │ │ 6 │ │ 7 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────┘
HDD vs SSD 비교:
┌─────────────────┬────────────────┬────────────────┐
│ 특성 │ HDD │ SSD │
├─────────────────┼────────────────┼────────────────┤
│ 순차 읽기 │ 150 MB/s │ 500-7000 MB/s │
├─────────────────┼────────────────┼────────────────┤
│ 랜덤 읽기 │ 0.5 MB/s │ 50-500 MB/s │
├─────────────────┼────────────────┼────────────────┤
│ 지연시간 │ 5-10 ms │ 0.02-0.1 ms │
├─────────────────┼────────────────┼────────────────┤
│ IOPS (랜덤) │ ~200 │ 10K-1M │
├─────────────────┼────────────────┼────────────────┤
│ 내충격성 │ 약함 │ 강함 │
├─────────────────┼────────────────┼────────────────┤
│ GB당 가격 │ $0.02 │ $0.10 │
└─────────────────┴────────────────┴────────────────┘
5. 메모리 대역폭과 지연시간¶
5.1 대역폭 (Bandwidth)¶
정의: 단위 시간당 전송 가능한 데이터 양 (bytes/second)
계산:
대역폭 = 버스 폭 × 전송 속도
예시 (DDR4-3200):
- 버스 폭: 64비트 = 8바이트
- 전송 속도: 3200 MT/s (Mega Transfers per second)
- 대역폭 = 8 × 3200 = 25,600 MB/s = 25.6 GB/s (채널당)
듀얼 채널: 25.6 × 2 = 51.2 GB/s
쿼드 채널: 25.6 × 4 = 102.4 GB/s
시스템별 메모리 대역폭:
┌─────────────────────────┬────────────────────────┐
│ 시스템 │ 대역폭 │
├─────────────────────────┼────────────────────────┤
│ 일반 데스크톱 (DDR4) │ 25-50 GB/s │
├─────────────────────────┼────────────────────────┤
│ 하이엔드 워크스테이션 │ 100-200 GB/s │
├─────────────────────────┼────────────────────────┤
│ 서버 (8채널 DDR5) │ 300-400 GB/s │
├─────────────────────────┼────────────────────────┤
│ GPU (HBM2) │ 1-2 TB/s │
└─────────────────────────┴────────────────────────┘
5.2 지연시간 (Latency)¶
정의: 데이터 요청부터 수신까지 걸리는 시간
메모리 계층별 지연시간:
레벨 │ 지연시간 (cycles) │ 지연시간 (ns) │ 상대 비용
────────────┼───────────────────┼───────────────┼───────────
레지스터 │ 1 │ ~0.3 │ 1x
L1 캐시 │ 4-5 │ ~1.5 │ 4x
L2 캐시 │ 12-14 │ ~4 │ 12x
L3 캐시 │ 40-50 │ ~15 │ 40x
메인 메모리 │ 100-300 │ ~60-100 │ 200x
SSD │ 10,000-50,000 │ 25-100us │ 50,000x
HDD │ 10,000,000+ │ 5-10ms │ 20,000,000x
시각화:
┌───────────────────────────────────────────────────────────┐
│ │
│ L1: ● │
│ L2: ●●● │
│ L3: ●●●●●●●●●●●●● │
│ RAM: ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● │
│ │
│ SSD: [...] (너무 길어 표시 불가) │
│ HDD: [...] (더더욱 표시 불가) │
│ │
└───────────────────────────────────────────────────────────┘
5.3 Latency vs Bandwidth 관계¶
Little's Law:
동시 요청 수 = 대역폭 × 지연시간
메모리 시스템에서의 의미:
- 대역폭 활용을 위해 다수의 동시 요청 필요
- 지연시간이 길면 더 많은 동시 요청 필요
예시:
- 대역폭: 50 GB/s
- 지연시간: 100 ns
- 캐시 라인: 64 bytes
필요한 동시 요청 수:
= (50 × 10^9) × (100 × 10^-9) / 64
= 78개 동시 요청
해결책:
- 비순차 실행으로 다수의 로드 동시 발행
- 프리페치로 미리 데이터 요청
- 메모리 레벨 병렬성 (MLP) 활용
5.4 AMAT (Average Memory Access Time)¶
공식:
AMAT = Hit Time + (Miss Rate × Miss Penalty)
예시:
- L1 히트 시간: 4 사이클
- L1 미스율: 5%
- L2 히트 시간: 12 사이클
- L2 미스율: 20% (L1 미스 중)
- 메모리 접근 시간: 200 사이클
계산:
AMAT = 4 + 0.05 × (12 + 0.20 × 200)
= 4 + 0.05 × (12 + 40)
= 4 + 0.05 × 52
= 4 + 2.6
= 6.6 사이클
다중 레벨 캐시의 AMAT:
┌──────────────────────────────────────────────────────────┐
│ AMAT = T_L1 + MR_L1 × (T_L2 + MR_L2 × (T_L3 + MR_L3 × T_Mem))│
└──────────────────────────────────────────────────────────┘
6. 메모리 성능 최적화¶
6.1 데이터 레이아웃 최적화¶
Structure of Arrays (SoA) vs Array of Structures (AoS):
AoS (Array of Structures):
struct Particle {
float x, y, z; // 12 bytes
float vx, vy, vz; // 12 bytes
float mass; // 4 bytes
int id; // 4 bytes
}; // 총 32 bytes
Particle particles[1000];
메모리 레이아웃:
[x,y,z,vx,vy,vz,mass,id][x,y,z,vx,vy,vz,mass,id][...]
x 좌표만 접근할 때: 32바이트마다 4바이트만 사용 → 낭비
SoA (Structure of Arrays):
struct Particles {
float x[1000];
float y[1000];
float z[1000];
float vx[1000];
float vy[1000];
float vz[1000];
float mass[1000];
int id[1000];
};
메모리 레이아웃:
[x0,x1,x2,...][y0,y1,y2,...][z0,z1,z2,...]...
x 좌표만 접근할 때: 연속된 메모리 접근 → 효율적
성능 차이: SoA가 최대 4-8배 빠를 수 있음 (SIMD 활용 시)
6.2 루프 최적화¶
루프 타일링 (Loop Tiling / Blocking):
// 원본 코드 - 캐시 효율 낮음
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
for (k = 0; k < N; k++)
C[i][j] += A[i][k] * B[k][j];
// 타일링 적용 - 캐시 효율 높음
#define BLOCK 64
for (ii = 0; ii < N; ii += BLOCK)
for (jj = 0; jj < N; jj += BLOCK)
for (kk = 0; kk < N; kk += BLOCK)
for (i = ii; i < min(ii+BLOCK, N); i++)
for (j = jj; j < min(jj+BLOCK, N); j++)
for (k = kk; k < min(kk+BLOCK, N); k++)
C[i][j] += A[i][k] * B[k][j];
효과:
┌──────────────────────────────────────────────────────────┐
│ 원본: 큰 행렬 전체를 반복 접근 → 캐시 스래싱 │
│ 타일링: 작은 블록을 완전히 처리 → 캐시에 유지 │
│ │
│ N=1024, BLOCK=64일 때: │
│ - 원본: ~10초 │
│ - 타일링: ~2초 (5배 향상) │
└──────────────────────────────────────────────────────────┘
6.3 프리페칭¶
하드웨어 프리페치:
- CPU가 접근 패턴을 감지하여 자동으로 프리페치
- 순차 접근, 스트라이드 접근에 효과적
- 불규칙한 접근에는 비효과적
소프트웨어 프리페치:
// Intel intrinsic 예시
for (i = 0; i < N; i++) {
_mm_prefetch(&array[i + 16], _MM_HINT_T0); // L1으로 프리페치
sum += array[i];
}
프리페치 거리 계산:
거리 = 메모리 지연시간 / 루프 반복당 시간
예시:
- 메모리 지연시간: 100 사이클
- 루프 반복당 시간: 5 사이클
- 프리페치 거리: 100 / 5 = 20 요소 앞서 프리페치
주의사항:
- 너무 빠른 프리페치: 캐시에서 밀려남
- 너무 느린 프리페치: 데이터 도착 안 됨
- 불필요한 프리페치: 캐시 오염
6.4 메모리 정렬¶
정렬(Alignment) 중요성:
정렬된 접근:
주소: 0x1000 (64바이트 정렬)
┌───────────────────────────────────────────────┐
│ 64바이트 캐시 라인 │
│ [데이터 64바이트가 한 라인에 모두 포함] │
└───────────────────────────────────────────────┘
접근 횟수: 1회
비정렬 접근:
주소: 0x1020 (32바이트 오프셋)
┌─────────────────────┬─────────────────────────┐
│ 캐시 라인 1 │ 캐시 라인 2 │
│ [...32바이트...] │ [...32바이트...] │
└─────────────────────┴─────────────────────────┘
접근 횟수: 2회
정렬 지시어:
// C/C++
struct alignas(64) CacheLine {
int data[16];
};
// 동적 할당
void* ptr = aligned_alloc(64, size);
7. 연습 문제¶
기초 문제¶
-
메모리 계층 구조가 필요한 이유를 설명하시오.
-
다음 코드에서 시간적 지역성과 공간적 지역성을 찾으시오:
c for (i = 0; i < 100; i++) { sum += array[i]; } -
SRAM과 DRAM의 주요 차이점 3가지는?
중급 문제¶
-
L1 캐시 히트 시간이 4사이클, 미스율이 8%, L2 접근 시간이 12사이클일 때 AMAT는?
-
다음 중 공간적 지역성을 해치는 코드는? ```c // (a) for (i = 0; i < N; i++) for (j = 0; j < N; j++) sum += a[i][j];
// (b) for (j = 0; j < N; j++) for (i = 0; i < N; i++) sum += a[i][j]; ```
- DDR4-3200 듀얼 채널의 이론적 최대 대역폭은?
심화 문제¶
-
Working Set 크기가 캐시 크기보다 클 때 발생하는 문제와 해결책을 설명하시오.
-
다음 행렬 곱셈 코드를 루프 타일링을 적용하여 최적화하시오:
c for (i = 0; i < N; i++) for (j = 0; j < N; j++) for (k = 0; k < N; k++) C[i][j] += A[i][k] * B[k][j]; -
프리페치 거리를 계산하시오:
- 메모리 지연시간: 60ns
- CPU 클럭: 3GHz
- 루프당 명령어: 10개, CPI: 1
정답
1. CPU와 메모리 간 속도 격차를 해소하기 위해. 빠르고 비싼 메모리를 CPU 가까이, 느리고 저렴한 메모리를 멀리 배치하여 비용 대비 성능 최적화. 2. - 시간적 지역성: 변수 `sum`, `i` (100번 반복 접근) - 공간적 지역성: `array[i]` (연속된 메모리 주소 접근) 3. SRAM vs DRAM: - 구조: SRAM 6T, DRAM 1T1C - 리프레시: SRAM 불필요, DRAM 필요 - 속도: SRAM 빠름 (~2ns), DRAM 느림 (~50ns) - 비용/밀도: SRAM 비쌈/낮음, DRAM 저렴/높음 4. AMAT = 4 + 0.08 × 12 = 4 + 0.96 = 4.96 사이클 5. (b) 열 우선 순회 - C/C++에서 배열은 행 우선 저장되므로 6. DDR4-3200: 8바이트 × 3200MT/s × 2채널 = 51.2 GB/s 7. 캐시 스래싱 발생 - 필요한 데이터가 반복적으로 교체됨 해결책: 루프 타일링, 데이터 레이아웃 최적화, 알고리즘 변경 8. 타일링 적용: ```c #define B 64 for (ii = 0; ii < N; ii += B) for (jj = 0; jj < N; jj += B) for (kk = 0; kk < N; kk += B) for (i = ii; i < ii+B && i < N; i++) for (j = jj; j < jj+B && j < N; j++) for (k = kk; k < kk+B && k < N; k++) C[i][j] += A[i][k] * B[k][j]; ``` 9. 프리페치 거리: - 메모리 지연: 60ns × 3GHz = 180 사이클 - 루프 시간: 10 명령어 × 1 CPI = 10 사이클 - 거리: 180 / 10 = 18 요소 앞서 프리페치다음 단계¶
- 15_Cache_Memory.md - 캐시 사상 방식, 교체 정책, 쓰기 정책
참고 자료¶
- Computer Architecture: A Quantitative Approach (Hennessy & Patterson)
- What Every Programmer Should Know About Memory (Ulrich Drepper)
- Intel Memory Latency Checker
- Memory hierarchy visualization