주소 변환 결과를 캐싱하는 작고 빠른 하드웨어 캐시.
없으면 어떻게 되나
다단계 페이지 테이블에서 주소 하나를 변환하려면
- 각 단계의 테이블을 메모리에서 읽어야 한다 (4단계면 4번)
- 실제 데이터를 읽는 건 그다음이니 총 5번
메모리 접근 한 번이 다섯 번이 된다 → 감당이 안 된다
TLB가 하는 일
[페이지 번호 → 프레임 번호] 쌍을 수십~수백 개 캐싱한다
- TLB 히트 — → 즉시 변환 (1 사이클 수준)
- TLB 미스 — → 페이지 테이블을 걸어 내려간다 (page table walk) 후 TLB 에 채운다
적중률이 보통 99% 를 넘는다 — 지역성 덕분이다
"작은데 왜 잘 맞나" — 프로그램이 같은 페이지를 반복해서 쓰기 때문이다(공간 지역성). 4KB 페이지 하나에 코드나 데이터가 꽤 많이 들어간다.
컨텍스트 스위치와의 관계
프로세스가 바뀌면 주소 변환 규칙 자체가 달라진다
- 예전에는 TLB 를 통째로 비웠다 (플러시)
- 전환 직후 TLB 미스가 쏟아진다 → 컨텍스트 스위치 간접 비용의 큰 부분
ASID/PCID
- TLB 항목에 주소 공간 식별자를 붙인다
- 프로세스가 바뀌어도 전부 버리지 않아도 된다
성능 문제로 드러나는 모습
증상: 데이터를 넓게 흩어 접근하는 워크로드가 유독 느리다
- (큰 해시 테이블 무작위 접근 · 포인터를 따라가는 자료구조)
원인: 접근할 때마다 다른 페이지 → TLB 미스 → page table walk
처방: 데이터를 모아 배치한다 (배열이 연결 리스트보다 빠른 이유의 일부)
- Huge Page 로 페이지 수를 줄인다
perf stat -e dTLB-load-misses,iTLB-load-misses ./app
캐시 계층 안에서의 위치
-
CPU 레지스터 — < 1 사이클
-
L1 캐시 · TLB — 수 사이클
-
L2 캐시 — 수십 사이클
-
L3 캐시 — 수십~백 사이클
-
메인 메모리 — 수백 사이클
-
디스크(SSD) — 수만~수십만 사이클
-
TLB 미스는 '메인 메모리를 여러 번 읽는 것' 이라 체감 비용이 크다
면접 함정
- ❌ "TLB는 데이터를 캐싱한다" → 주소 변환 결과를 캐싱한다. 데이터는 L1/L2/L3의 몫이다.
- ❌ "TLB 미스는 페이지 폴트" → 다르다. TLB 미스는 테이블에 있고, 페이지 폴트는 메모리에 없는 것이다.
크기와 구조
보통 L1 TLB 수십~수백 항목, L2 TLB 수천 항목 명령용(iTLB)과 데이터용(dTLB)이 분리돼 있다
4KB 페이지 · 항목 1,536개면 커버 범위는 약 6MB
- 워킹셋이 그보다 크면 TLB 미스가 늘기 시작한다
"메모리는 충분한데 왜 느리지" 의 답이 여기일 때가 있다 — 용량이 아니라 접근 범위가 문제다.
Huge Page의 효과
2MB 페이지를 쓰면 같은 항목 수로 512배 넓은 범위를 덮는다
- 1,536 × 2MB = 약 3GB
큰 힙을 쓰는 DB · 인메모리 캐시에서 수 % ~ 수십 % 개선 사례가 있다
cat /sys/kernel/mm/transparent_hugepage/enabled
# [always] madvise never
# THP(투명 huge page)는 자동이지만 부작용도 있다
# 할당 지연 스파이크 · 메모리 낭비
# → DB 벤더들이 흔히 never 나 madvise 를 권장한다
보안과의 접점
Meltdown 취약점 대응으로 커널과 유저의 페이지 테이블을 분리했다 (KPTI)
→ 시스템 콜마다 페이지 테이블을 바꾼다 → TLB 플러시
→ 시스템 콜이 잦은 워크로드에서 성능이 눈에 띄게 떨어졌다
→ PCID 를 지원하는 CPU 에서는 손실이 크게 줄어든다