컴퓨터 구조 · 메모리 계층 · 이론
메모리 계층 — 빠른 것은 작고, 큰 것은 느리다
한 줄 요약
빠르면서 크고 싼 메모리는 만들 수 없으므로, 작고 빠른 것부터 크고 느린 것까지 층층이 쌓고 자주 쓰는 데이터를 위층으로 끌어올린다.
왜 이게 필요했나
레지스터는 CPU 와 같은 속도로 동작하지만 수십 개뿐이다. DRAM 은 수십 기가바이트를 담을 수 있지만 접근에 수백 사이클이 걸린다. 이 간극은 좁아지기는커녕 세대마다 벌어졌다. 연산 유닛을 늘리는 것은 트랜지스터를 더 넣으면 되지만, 데이터를 칩 안팎으로 나르는 능력은 핀 개수와 배선의 물리적 한계에 묶여 있기 때문이다.
이 누적된 간극을 메모리 월(memory wall) 이라고 한다. 계층 구조는 이 벽을 우회하는 유일한 현실적 방법이다.
어떻게 동작하나
전형적인 계층과 대략의 지연은 이렇다. 숫자는 세대마다 다르지만 자릿수의 차이가 요점이다.
| 계층 | 크기 | 접근 지연 (대략) |
| --- | --- | --- |
| 레지스터 | 수백 바이트 | 1사이클 미만 |
| L1 캐시 | 32~64KB | 4~5사이클 |
| L2 캐시 | 0.5~2MB | 12~20사이클 |
| L3 캐시 | 수십 MB | 40~70사이클 |
| DRAM | 수십 GB | 200~300사이클 |
| NVMe SSD | 수 TB | 수만 사이클 |
이 구조가 실제로 작동하는 이유는 프로그램이 지역성(locality) 을 갖기 때문이다.
- 시간 지역성 — 한 번 쓴 데이터는 곧 다시 쓰일 가능성이 높다. 반복문의 변수가 그렇다.
- 공간 지역성 — 한 주소를 쓰면 그 근처 주소도 곧 쓰일 가능성이 높다. 배열 순회가 그렇다.
캐시는 이 두 성질을 그대로 이용한다. 데이터를 한 바이트씩 가져오지 않고 캐시 라인 단위, 보통 64바이트를 통째로 가져온다. 4바이트 정수 하나를 읽어도 이웃한 15개가 함께 올라온다. 배열을 순서대로 도는 코드가 빠른 이유가 이것이다.
캐시 미스는 세 종류로 나눠 보면 대응이 갈린다.
- 강제 미스(compulsory) — 처음 접근하는 데이터. 프리페치로만 완화된다.
- 용량 미스(capacity) — 작업 집합이 캐시보다 커서 밀려남. 알고리즘이나 데이터 구조를 바꿔야 한다.
- 충돌 미스(conflict) — 같은 세트에 몰려서 밀려남. 배열의 보폭(stride)이 캐시 크기의 배수일 때 잘 생긴다.
현장에서 만나는 모습
행 우선으로 저장된 2차원 배열을 열 우선으로 순회하면 같은 연산인데 수 배에서 수십 배 느려진다. 매 접근마다 새 캐시 라인을 가져오고 그중 4바이트만 쓰고 버리기 때문이다. 행렬 곱셈에서 타일링(블로킹)을 쓰는 이유도 같다. 작업 집합을 캐시 안에 들어가는 크기로 잘라 재사용을 늘리는 것이다.
AI 워크로드에서는 이 문제가 더 노골적으로 드러난다. LLM 추론의 디코딩 단계는 거대한 가중치를 한 번 읽어와 작은 입력과 곱하고 버린다. 산술 강도(가져온 바이트당 수행하는 연산 수)가 극도로 낮아서, 연산기는 대부분 놀고 메모리 대역폭이 성능을 결정한다. GPU 사용률이 30퍼센트를 못 넘는다는 흔한 하소연의 정체가 이것이다. 이때 더 비싼 연산 칩을 사는 것은 돈만 쓰는 선택이고, 답은 읽을 바이트를 줄이거나(양자화) 재사용을 늘리는 것이다.
이어지는 퀴즈에서 확인할 것
캐시 라인이 왜 64바이트씩 움직이는지, 같은 알고리즘이 접근 순서만 바꿨는데 왜 배수로 느려지는지 설명할 수 있는지 확인한다.