Chapter 05

캐시와 메모리 계층

4 GHz로 도는 CPU 코어에게 1클럭은 0.25 ns다. 이 코어가 DRAM에 값 하나를 물어보면 대답이 오기까지 100 ns 남짓, 즉 400클럭이 넘게 걸린다. 1클럭을 1초로 늘려 생각하면, 계산은 1초마다 한 번씩 척척 하는데 재료를 가지러 창고에 한 번 다녀오는 데 7분이 걸리는 셈이다. 그런데도 실제 프로그램이 그럭저럭 빠르게 도는 것은 코어 바로 옆에 작고 빠른 기억 장치, 캐시(Cache)가 층층이 놓여 있기 때문이다. 이 장에서는 캐시를 이루는 SRAM 셀 하나에서 출발해, 주소를 쪼개 데이터를 찾는 법, 미스가 생기는 세 가지 이유, 그리고 소프트웨어가 캐시와 손발을 맞추는 법까지 직접 돌려 보며 익힌다.

기다림의 사다리

컴퓨터의 기억 장치는 한 가지가 아니다. 빠른 것은 비싸고 작으며, 크고 싼 것은 느리다. 이 둘을 동시에 가질 수 없으니, 설계자는 여러 종류를 층층이 쌓아 메모리 계층(Memory hierarchy)을 만든다. 맨 위의 레지스터는 코어가 매 클럭 바로 쓰는 수십 개의 칸이고, 그 아래로 L1, L2, L3 캐시, 칩 전체가 함께 쓰는 시스템 캐시(SLC), 칩 밖의 DRAM, 그리고 전원을 꺼도 지워지지 않는 플래시 저장장치가 이어진다.

각 층의 지연을 막대로 늘어놓으면 한 화면에 담기지 않는다. 그래서 아래 그림은 가로축을 로그 눈금으로 그렸다. 한 칸이 10배다. 층을 눌러 보고, "사람 시간"으로 바꿔 1클럭(0.25 ns)을 1초로 늘렸을 때 각 층이 얼마나 먼지 느껴 보자.

고른 층—
접근 지연—
4 GHz 클럭으로—
사람 시간으로—
전형적 용량—
그림 5-1. 만져 보기막대를 누르면 그 층까지 다녀오는 요청(점)이 움직인다. 지연은 2023~2025년 스마트폰 큰 코어 기준의 대략값이다(L1 4클럭, L2 약 14클럭, L3 약 40클럭, SLC 약 25 ns, LPDDR5X 약 110 ns, UFS 무작위 읽기 약 60 µs). 사람 시간으로 보면 L1은 4초, DRAM은 7분, 플래시는 사흘 가까이 걸린다.

사다리에서 눈여겨볼 것은 간격이 일정하지 않다는 점이다. L1에서 L3까지는 열 배 남짓 느려지지만, 칩 밖 DRAM으로 나가는 순간 다시 열 배, 플래시로 가면 거기서 또 수백 배가 뛴다. 칩 경계를 넘는 일이 그만큼 비싸다(1장에서 본 "데이터 이동이 비싸다"는 원칙). 그래서 캐시의 목표는 단순하다. 코어가 원하는 데이터를 가능한 한 사다리 위쪽에서 찾게 하는 것.

왜 그냥 큰 SRAM 하나를 두지 않나?

SRAM이 커질수록 배선이 길어지고, 주소를 해독하고 비트라인을 충전하는 데 시간이 더 든다. 대략 용량이 4배가 되면 접근 시간이 2배쯤 는다. 64 KB짜리 L1은 1 ns 안에 답하지만 16 MB짜리 L3가 같은 속도를 낼 수는 없다. 작고 빠른 것과 크고 느린 것을 섞는 계층 구조가 이 물리적 한계에 대한 답이다.

캐시는 무엇으로 만드나: 6T SRAM 셀

캐시의 비트 하나는 트랜지스터 여섯 개로 만든 SRAM 셀(Static RAM cell, 6T)에 저장된다. 가운데에 인버터 두 개가 서로의 출력을 입력으로 물고 있다. 한쪽 노드 Q가 1이면 반대쪽 QB는 0이고, 이 상태는 전원이 있는 한 스스로 유지된다(그래서 "정적" RAM이다. DRAM처럼 새로 고칠 필요가 없다. 7장). 나머지 트랜지스터 두 개는 접근 트랜지스터로, 가로로 지나가는 워드라인(Wordline, WL)이 켜질 때만 셀을 세로 방향의 비트라인(Bitline, BL/BLB) 한 쌍에 연결한다.

아래 셀을 직접 조작해 보자. WL 선을 누르면 워드라인이 켜지고 꺼진다. 비트라인 아래의 상자를 누르면 그 비트라인을 "프리차지(1로 미리 충전 후 떠 있음)"와 "0으로 강하게 구동" 사이에서 바꾼다. 버튼은 이 조작을 정해진 순서로 대신 해 준다.

저장된 값 Q—
비트라인 전압 차 ΔV—
감지 증폭기 출력—
그림 5-2. 만져 보기읽기는 두 비트라인을 VDD로 충전한 뒤 WL을 켠다. 0을 저장한 쪽 노드가 접근 트랜지스터를 통해 자기 비트라인 전하를 아주 조금씩 빼낸다. 비트라인은 수백 개 셀이 매달린 긴 선이라 정전 용량이 커서 전압이 천천히 내려간다. 차이가 100 mV 남짓 벌어지면 감지 증폭기가 그것을 완전한 0/1로 키운다. 쓰기는 한쪽 비트라인을 0으로 세게 눌러 셀의 상태를 강제로 뒤집는다.

읽기에서 비트라인 전압이 끝까지 내려가기를 기다리지 않는다는 점이 중요하다. 감지 증폭기는 수십~100 mV의 작은 차이만 보고 판단하므로, 그만큼 빨리 읽을 수 있다. 대신 셀 트랜지스터의 크기 비율을 신중하게 맞춰야 한다. 읽는 동안 0 쪽 노드가 비트라인 쪽으로 끌려 올라가 값이 뒤집히면 안 되고(읽기 안정성), 쓰는 동안에는 접근 트랜지스터가 셀 안의 PMOS를 이길 만큼 세야 한다(쓰기 능력). 공정이 미세해질수록 트랜지스터 특성의 편차가 커져 이 균형을 맞추기 어려워진다.

SRAM은 공정 미세화의 혜택을 덜 받는다

로직 트랜지스터 밀도는 5 nm에서 3 nm로 가며 1.5배 이상 좋아졌지만, 고밀도 SRAM 셀 면적은 약 0.021 µm²에서 거의 줄지 않았다(2023년 공개 자료 기준). 그래서 SoC에서 캐시가 차지하는 면적 비율이 점점 커지고, 큰 캐시를 별도 다이에 쌓는 3D 적층 캐시 같은 기법이 등장했다(17장).

셀 하나가 1비트이니, 64 KB L1 데이터 캐시에는 데이터 셀만 52만 개가 넘고, 여기에 뒤에서 볼 태그·상태 비트가 더해진다. 셀들은 수백 행 × 수백 열의 작은 배열(서브어레이)로 묶이고, 주소의 일부가 행(워드라인)을, 일부가 열을 고른다.

캐시가 통하는 이유: 지역성

캐시는 메인 메모리의 아주 작은 일부만 담는다. 스마트폰 DRAM이 12 GB라면 L1은 그 20만분의 1도 안 된다. 그런데도 실제 프로그램에서 L1 적중률은 흔히 95%를 넘는다. 이유는 프로그램의 메모리 접근이 무작위가 아니기 때문이다.

캐시는 공간 지역성을 이용하려고 바이트 하나가 아니라 캐시 줄(Cache line, 블록) 단위로 데이터를 가져온다. 요즘 CPU는 대부분 64바이트, 애플의 일부 코어는 128바이트 줄을 쓴다. 아래는 여러 코드 패턴이 메모리를 건드리는 모습이다. 가로가 시간, 세로가 주소이고, 점 색은 1 KB짜리 작은 캐시에서 적중(초록)했는지 미스(빨강)였는지를 뜻한다.

적중률—
건드린 캐시 줄—
가져온 바이트 중 실제 사용—
그림 5-3. 만져 보기4바이트 정수 256번 접근, 캐시는 1 KB(완전 연관, LRU). "행렬"은 32×32 정수 배열(행마다 128 B)을 행 우선으로 저장한 것이다. 행 순회는 이웃한 주소를 차례로 밟아 줄 하나에서 여러 번 적중하지만, 열 순회는 매번 128 B씩 건너뛰어 줄을 하나씩 낭비한다. 줄 크기를 바꿔 보자. 순차 접근은 줄이 클수록 좋아지지만, 무작위 접근은 오히려 가져온 데이터 대부분을 버린다.
행렬 열 순회에서 줄 크기를 16 B에서 256 B로 키우면 적중률이 오를까?

행 하나가 128 B이고, 열 순회는 같은 열을 위에서 아래로 32번 내려간 뒤 다음 열로 간다.

답 보기

잠깐 오르다가 다시 망가진다. 줄이 128 B 이상이면 줄 하나에 한 행 전체(또는 두 행)가 들어가므로, 다음 열로 넘어갔을 때 앞에서 가져온 32개 행의 줄이 아직 캐시에 남아 있으면 적중한다. 32행 × 128 B = 4 KB가 필요한데 캐시는 1 KB뿐이라 그 전에 쫓겨난다. 줄이 커지면 줄 수가 줄어(1 KB/256 B = 4줄) 더 빨리 쫓겨난다. 같은 용량에서 줄을 키우면 "줄 수"가 줄어든다는 것이 줄 크기의 숨은 비용이다. 이 문제는 뒤의 타일링 절에서 소프트웨어로 푼다.

주소를 쪼개 데이터를 찾는다

캐시는 "이 주소의 데이터가 내 안에 있나?"를 1 ns 안에 답해야 한다. 모든 칸을 하나씩 뒤질 시간은 없다. 그래서 주소를 세 조각으로 나눈다.

한 세트에 줄을 몇 개 둘 수 있는지가 연관도(Associativity, way 수)다. 1이면 직접 사상(Direct-mapped), 세트가 하나뿐이고 모든 줄이 어디든 들어갈 수 있으면 완전 연관(Fully associative), 그 사이가 A-way 집합 연관이다. A-way 캐시는 세트를 고른 뒤 A개의 태그를 동시에 비교한다. 비교기가 A개 필요하고 전력이 들지만, 같은 세트로 몰리는 주소끼리 서로 쫓아내는 일이 줄어든다.

$$ S = \frac{C}{B \cdot A}, \qquad b_{\text{offset}} = \log_2 B, \quad b_{\text{index}} = \log_2 S, \quad b_{\text{tag}} = n - b_{\text{index}} - b_{\text{offset}} $$
\(C\): 용량(바이트), \(B\): 줄 크기, \(A\): 연관도, \(S\): 세트 수, \(n\): 주소 비트 수(여기서는 32)
세트 수 S—
태그 · 인덱스 · 오프셋—
이 주소의 세트—
태그 저장 부담—
그림 5-4. 만져 보기위의 비트 칸을 눌러 주소를 바꿔 보자. 파랑이 태그, 분홍이 인덱스, 노랑이 오프셋이다. 아래 격자는 세트들이고 밝은 칸이 이 주소가 가는 세트다(세트가 많으면 한 칸이 여러 세트를 대표한다). "+S·B"를 누르면 인덱스는 그대로이고 태그만 바뀐 주소가 된다. 이런 주소들이 A개보다 많이 동시에 쓰이면 서로를 쫓아낸다. 태그 저장 부담은 줄마다 (태그 + 유효·더티 2비트)를 데이터 비트와 비교한 값이다.

연관도를 1에서 16으로 올리면 세트 수가 1/16로 줄고, 인덱스 비트 4개가 태그로 넘어간다. 줄 크기를 키우면 오프셋이 늘고 줄 수가 줄어 태그 부담이 작아진다. 이처럼 세 매개변수는 서로 얽혀 있다. 실제 L1은 4~8-way, L2는 8~16-way, 큰 L3·SLC는 12~16-way 정도가 흔하다. L1이 연관도를 무작정 키우지 못하는 이유는 태그 비교와 데이터 선택이 1클럭 남짓에 끝나야 하기 때문이다.

캐시 시뮬레이터와 미스의 세 가지 이유

이제 부품을 모아 캐시 하나를 돌려 보자. 미스는 세 종류로 나누면 원인과 처방이 분명해진다. Mark Hill이 정리한 3C 모델이다.

SIMULATOR

집합 연관 캐시 시뮬레이터

용량
줄 크기
연관도
교체 정책
적중강제 미스용량 미스충돌 미스격자 칸 밝기 = 최근에 쓴 정도
버튼을 눌러 접근을 시작하자.
접근0
적중률—
강제 미스0
용량 미스0
충돌 미스0
원소는 4바이트 정수다. 미스를 분류하려고 같은 줄 수의 완전 연관 LRU 캐시를 그림자로 함께 돌린다. 처음 보는 줄이면 강제, 그림자 캐시에서도 미스면 용량, 그림자에서는 적중인데 이 캐시에서 미스면 충돌이다. "두 배열" 패턴은 b가 a에서 정확히 캐시 용량의 배수만큼 떨어져 있어, a[i]와 b[i]가 늘 같은 세트로 간다.
"두 배열 번갈아" 패턴에서 작업 집합이 캐시의 0.75배인데 직접 사상 캐시는 적중률이 거의 0이다. 연관도를 2로 올리면?

a[i]와 b[i]는 인덱스가 같고 태그만 다르다.

답 보기

충돌 미스가 사라져 적중률이 줄 하나에 든 원소 수만큼 오른다(32 B 줄이면 8개 중 7개 적중, 약 87%). 직접 사상에서는 a[i]를 가져오면 b[i]가 같은 자리를 차지하고 있던 a의 줄을 쫓아내고, 다음 a[i+1]이 다시 b의 줄을 쫓아내는 핑퐁이 일어난다. 2-way면 두 줄이 한 세트에 함께 산다. 이처럼 배열 크기가 2의 거듭제곱일 때 충돌이 잘 생기므로, 실무에서는 배열 사이에 일부러 빈칸(패딩)을 넣기도 한다. 작업 집합을 1.5배 이상으로 키우면 이번에는 연관도를 올려도 용량 미스가 남는다. 직접 확인해 보자.

교체 정책

세트가 꽉 찼을 때 누구를 내보낼지 정하는 것이 교체 정책(Replacement policy)이다. LRU(Least recently used)는 가장 오래 쓰지 않은 줄을 내보낸다. 시간 지역성에 잘 맞지만, 정확한 LRU 순서를 저장하려면 way가 많을수록 비트가 많이 들어 실제로는 트리 의사 LRU 같은 근사를 쓴다. FIFO는 가장 먼저 들어온 줄을, 무작위는 아무 줄이나 내보낸다. 시뮬레이터에서 "배열 반복 훑기"의 작업 집합을 캐시보다 조금 크게(1.25배) 하고 LRU와 무작위를 비교해 보자. 놀랍게도 LRU가 0%로 망가진다. 순환 접근에서는 LRU가 언제나 "곧 다시 쓸 줄"을 골라 내보내기 때문이다. 최근 캐시가 RRIP 같은 적응형 정책을 쓰는 이유다.

쓰기는 어떻게 하나: 즉시 쓰기와 나중 쓰기

읽기는 캐시에 사본을 두기만 하면 되지만, 쓰기는 문제가 생긴다. 캐시의 값을 바꾸면 메모리의 원본과 달라진다. 해결책은 두 가지다.

아래는 4줄짜리 직접 사상 캐시 두 개다. 같은 명령을 두 캐시에 동시에 보내고 메모리 트래픽을 비교한다. A와 E, B와 F, C와 G, D와 H가 각각 같은 세트를 쓴다.

즉시 쓰기: 메모리로 쓴 바이트0
나중 쓰기: 메모리로 쓴 바이트0
줄 가져오기 (즉시 / 나중)0 / 0
주소 버튼을 눌러 보자.
그림 5-5. 만져 보기각 주소에는 숫자 하나가 들어 있고 쓰기는 그 값을 1 올린다. 줄 크기 64 B, 쓰기 한 번은 8 B로 가정했다. "D" 표시가 더티 줄이다. 나중 쓰기 캐시 아래의 메모리 값이 주황색이면 캐시의 값과 다른 낡은 값이라는 뜻이다. 다른 코어나 DMA 장치가 이 메모리를 그대로 읽으면 틀린 값을 보게 된다. 이 문제가 6장의 주제다.

스마트폰 SoC의 L1 데이터 캐시와 L2·L3는 거의 모두 나중 쓰기다. 다만 L1과 L2 사이에는 쓰기 버퍼(Write buffer)를 두어, 코어가 쓰기가 끝나기를 기다리지 않고 다음 명령으로 넘어가게 한다. 이 버퍼가 메모리 순서를 미묘하게 바꾸는 이야기도 6장에서 다룬다.

여러 층을 합치면: 평균 메모리 접근 시간

캐시 계층의 성능은 평균 메모리 접근 시간(AMAT, Average memory access time) 하나로 요약할 수 있다. L1에서 적중하면 L1 지연만, 미스하면 그 아래 층에서 걸리는 시간이 더해진다. 아래 층에도 똑같은 식을 재귀적으로 적용한다.

$$ \text{AMAT} = t_{L1} + m_{L1}\Big(t_{L2} + m_{L2}\big(t_{L3} + m_{L3}\, t_{\text{DRAM}}\big)\Big) $$
\(t\): 각 층의 적중 지연, \(m\): 각 층의 지역 미스율(그 층까지 내려온 요청 중 미스 비율). L1 미스 5%, L2 지역 미스 30%, L3 지역 미스 40%면 DRAM까지 가는 비율은 \(0.05 \times 0.3 \times 0.4 = 0.6\%\)다.
AMAT—
DRAM까지 가는 접근—
캐시가 없을 때보다—
그림 5-6. 만져 보기왼쪽 점 1,000개는 메모리 접근 1,000번이 각각 어느 층에서 해결되었는지 보여 준다. 오른쪽 막대는 AMAT를 층별 기여로 나눈 것이다. 지연은 4 GHz 기준 L1 4, L2 14, L3 45클럭. DRAM 점은 몇 개 안 되지만 막대에서는 큰 몫을 차지한다. L3를 끄면 L2 미스가 모두 DRAM으로 간다.

AMAT 막대를 보면 왜 캐시 설계가 어려운지 알 수 있다. DRAM까지 가는 접근이 1%도 안 되는데 시간은 수십 %를 차지한다. 그래서 마지막 층의 미스율을 몇 %포인트 줄이는 일이 L1 지연을 1클럭 줄이는 것만큼, 혹은 그보다 더 중요할 수 있다. 실제 코어는 비순차 실행(4장)으로 미스를 기다리는 동안 다른 명령을 실행하고, 여러 미스를 동시에 보내서(메모리 수준 병렬성) 지연의 일부를 숨긴다. 그래도 숨길 수 없는 DRAM 지연이 많은 프로그램의 성능 한계가 된다.

계층전형적 용량지연 (대략)연관도·줄공유 범위
L1 명령·데이터코어당 32~128 KB씩 (애플 큰 코어는 약 128~192 KB)3~5클럭 (~1 ns)4~8-way, 64 B코어 하나
L2코어당 0.5~2 MB, 또는 클러스터 공유 4~16 MB10~18클럭8~16-way코어 하나 또는 클러스터
L34~16 MB30~50클럭 (~10 ns)12~16-wayCPU 클러스터 전체
시스템 캐시 (SLC)4~32 MB~20~35 ns16-way 안팎CPU·GPU·NPU·ISP 등 칩 전체
LPDDR5X DRAM8~24 GB~100~150 ns—칩 전체

표의 값은 2023~2025년 플래그십 스마트폰 칩의 공개 사양·분석 자료를 바탕으로 한 대략값이다. 예를 들어 Arm Cortex-X4(2023)는 L1 명령·데이터 각 64 KB, 코어 전용 L2 최대 2 MB를 두고, 클러스터 공유 L3는 DSU에서 최대 32 MB까지 구성할 수 있다. 회사마다 층을 나누는 방식이 달라서, 애플처럼 L2를 클러스터가 크게 공유하고 L3 없이 SLC로 가는 설계도 있다. SB-1은 L3 약 4.8 mm²와 SLC 약 5.3 mm²를 합쳐 다이의 10%가량을 캐시에 쓴다.

SLC는 누구를 위한 캐시인가

CPU 입장에서 SLC는 L4쯤 되는 캐시지만, 진짜 고객은 GPU·NPU·ISP·디스플레이 같은 다른 블록이다. 이들이 주고받는 중간 데이터(예: ISP 출력을 NPU가 읽는 것)를 SLC에서 해결하면 DRAM까지 가지 않아 에너지를 아낀다. DRAM 한 번 읽기는 SRAM 읽기보다 수십 배 비싸다. 그래서 SLC는 성능 못지않게 전력을 위한 장치다. 블록마다 SLC 공간을 나눠 주는 파티셔닝도 흔하다.

소프트웨어와 하드웨어가 손을 맞추면: 타일링과 프리페치

같은 계산이라도 데이터를 건드리는 순서만 바꾸면 미스가 몇 배 줄어든다. 가장 유명한 예가 행렬 곱셈의 타일링(Tiling, Blocking)이다. \(C = A \times B\)를 그대로 계산하면 B를 열 방향으로 훑어야 해서, 행렬이 캐시보다 크면 B의 줄이 재사용되기 전에 쫓겨난다. 행렬을 T×T 크기의 작은 타일로 나눠, 타일 세 개(A, B, C 하나씩)가 캐시에 들어가게 하면 한 번 가져온 데이터를 T번씩 재사용할 수 있다.

SIMULATOR

행렬 곱셈 타일링 (64×64, double)

L1 데이터 캐시 용량 (4-way, 64 B 줄)
메모리 접근—
캐시 미스—
아래 계층 트래픽—
타일 없을 때 대비 시간—
세 행렬(각 32 KB)이 메모리에 연이어 놓인 상태에서, 타일 루프(ii, jj, kk) 안에서 i, j, k 순서로 C[i][j] += A[i][k]·B[k][j]를 계산하며 모든 읽기를 실제로 캐시 시뮬레이터에 통과시킨 결과다(약 55만 번 접근). 점선은 타일 세 개가 캐시에 들어가는 한계 \(3T^2 \times 8\,\text{B} \le C\)이다. 시간은 적중 1클럭, 미스 30클럭(L2 적중 가정)으로 어림했다. 오른쪽 그림은 지금 계산 중인 타일 위치를 보여 준다.

타일이 너무 작으면 재사용 횟수 T가 작아 이득이 적고, 너무 크면 타일 세 개가 캐시를 넘쳐 다시 미스가 늘어난다. 그래프의 바닥이 대략 점선 근처에 있는 것을 확인하자. 고성능 수학 라이브러리(BLAS)는 이런 타일링을 L1, L2, L3 각 층에 대해 겹겹이 하고, NPU 컴파일러도 같은 일을 온칩 SRAM 크기에 맞춰 한다(10장).

하드웨어가 미리 가져오기: 프리페처

소프트웨어를 고치지 않아도 하드웨어가 접근 패턴을 알아채고 데이터를 미리 가져올 수 있다. 이것이 프리페처(Prefetcher)다. 가장 기본적인 보폭 프리페처(Stride prefetcher)는 주소가 일정한 간격(보폭)으로 늘어나는 것을 두 번 연속 보면, 같은 간격으로 앞의 줄 몇 개를 미리 요청한다. 얼마나 멀리 앞서 요청할지가 프리페치 거리다.

프리페치 끔—
프리페치 켬—
빨라진 배수—
미스 감춤 / 헛된 요청—
그림 5-7. 만져 보기8 B 원소 512개를 처리하는 시간을 두 줄로 비교했다. 연한 칸은 계산, 빨간 칸은 데이터를 기다리며 멈춘 시간이다. 메모리 지연 200클럭, 줄 하나를 받는 데 버스가 8클럭씩 쓴다고 가정했다. 계산 시간이 짧을수록 프리페치 거리를 늘려야 지연이 다 가려진다. 무작위 접근에서는 보폭을 찾지 못해 프리페처가 거의 쉬거나, 우연히 잘못 맞춘 보폭으로 헛된 요청을 보낸다.

프리페치 거리는 \(d \approx \lceil t_{\text{mem}} / t_{\text{line}} \rceil\)이면 충분하다. 여기서 \(t_{\text{line}}\)은 줄 하나를 소비하는 데 걸리는 계산 시간이다. 너무 멀리 앞서 가져오면 아직 쓰지 않은 데이터가 캐시를 차지해 쓸모 있는 줄을 쫓아내고(캐시 오염), 쓸모없는 요청이 DRAM 대역폭을 낭비한다. 실제 코어에는 보폭, 공간 패턴, 포인터 추적 등 여러 프리페처가 함께 있고, 정확도를 스스로 측정해 공격성을 조절한다.

핵심 정리

  1. 메모리 계층은 레지스터 → L1 → L2 → L3 → SLC → DRAM → 플래시로 갈수록 크고 느리다. 칩 밖 DRAM은 L1보다 약 100배, 플래시는 다시 수백 배 느리다.
  2. 캐시 비트는 6T SRAM 셀에 저장한다. 읽기는 프리차지한 비트라인의 작은 전압 차를 감지 증폭기로 키우고, 쓰기는 비트라인 하나를 0으로 눌러 셀을 뒤집는다.
  3. 주소는 태그·인덱스·오프셋으로 나뉜다. \(S = C/(B\cdot A)\). 연관도를 올리면 충돌 미스가 줄고, 줄을 키우면 공간 지역성을 더 쓰지만 줄 수가 준다.
  4. 미스는 강제·용량·충돌(3C)로 나누면 처방이 보인다. 성능은 \(\text{AMAT} = t_{\text{hit}} + m \cdot t_{\text{miss}}\)를 층마다 재귀적으로 적용해 어림한다.
  5. 타일링으로 작업 집합을 캐시에 맞추고, 프리페처로 지연을 감추고, 나중 쓰기로 쓰기 트래픽을 줄인다. 데이터를 덜 움직이는 것이 곧 성능이고 전력이다.

확인 퀴즈

Q1. 32 KB, 8-way, 64 B 줄 캐시의 세트 수와 인덱스 비트 수는?

\(S = 32768 / (64 \times 8) = 64\), \(\log_2 64 = 6\). 오프셋은 6비트, 32비트 주소라면 태그는 20비트다.

Q2. 같은 용량의 완전 연관 캐시로 바꿨더니 사라지는 미스는?

충돌 미스는 "완전 연관이었다면 적중했을 미스"로 정의된다. 강제 미스는 무한 캐시에서도, 용량 미스는 같은 크기의 완전 연관 캐시에서도 남는다.

Q3. 6T SRAM 셀을 읽을 때 비트라인 전압이 끝까지 내려가기 전에 값을 결정할 수 있는 이유는?

셀 트랜지스터는 작아서 긴 비트라인을 천천히 방전시킨다. 감지 증폭기 덕분에 작은 차이만 생기면 바로 읽을 수 있어 접근 시간이 짧아진다.

Q4. L1 적중 4클럭, L1 미스율 10%, L2 적중 12클럭, L2 지역 미스율 50%, DRAM 400클럭(L3 없음)일 때 AMAT는?

\(4 + 0.1 \times (12 + 0.5 \times 400) = 4 + 0.1 \times 212 = 25.2\). 미스율 10% 중 절반이 DRAM으로 가며 평균 시간 대부분을 차지한다.

Q5. 나중 쓰기(write-back) 캐시에서 더티 비트가 하는 일은?

캐시에서만 값을 바꾼 줄은 메모리보다 새롭다. 더티 비트가 켜진 줄만 내보낼 때 메모리에 쓰면 되므로, 같은 줄에 여러 번 써도 메모리 쓰기는 한 번이다.

Q6. 행렬 곱셈을 타일링하는 주된 목적은?

곱셈 횟수(\(N^3\))는 그대로다. 타일 세 개가 캐시에 들어가면 각 원소를 T번 재사용하므로 용량 미스가 크게 준다. 강제 미스는 데이터를 처음 가져올 때 생기므로 그대로 남는다.