캐시 메모리의 지역성·매핑·쓰기 정책

CPU와 메모리의 속도 차이를 줄이는 캐시 메모리의 지역성, 계층 구조, 매핑·교체·쓰기 정책과 일관성 관리 방식을 정리한다.

2026-08-15 · 최초 발행 2026-01-04

CPU가 메모리를 기다리게 되는 지점

CPU의 처리 속도는 메모리 접근 속도보다 수백 배 빠르다. 이 차이는 시스템 성능에서 병목으로 작용하며, 캐시 메모리는 CPU와 주기억장치 사이에서 자주 사용하는 데이터를 임시 보관해 평균 메모리 접근 시간을 줄인다.

1 cycle3-4 cycles10-20 cycles50-200 cyclesMillions of cyclesCPU RegisterL1 CacheL2 CacheL3 CacheMain MemoryDisk Storage

프로세서는 GHz 단위로 동작하지만 DRAM 접근에는 수십 나노초가 걸린다. CPU는 메모리보다 100-200배 빠르고, 메모리 버스의 대역폭도 병목 요인이 될 수 있다. 이런 성능 간극이 계속 커지는 현상을 메모리 월(Memory Wall)이라 한다.

캐시는 CPU와 메모리의 속도 차이를 완화하고, 히트 시 빠른 응답을 제공한다. 메모리 버스 트래픽과 메모리 접근 횟수를 낮춰 대역폭과 전력 소비 측면에도 영향을 주며, AMAT를 개선하는 역할을 맡는다.

지역성을 이용해 데이터를 가까이 두는 방식

캐시가 성능을 내는 근거는 프로그램의 접근 패턴에 있다. 시간적 지역성은 최근에 접근한 데이터를 곧 다시 사용하는 특성이고, 공간적 지역성은 인접한 메모리 위치를 연속해서 읽는 특성이다. 명령어의 순차 실행, 반복문에서의 코드와 데이터 재사용, 배열의 연속 영역 접근이 대표적인 경우다.

for (int i = 0; i < 1000; i++) {  // 'i' 변수 반복 접근
    sum += array[i];               // 'sum' 변수 반복 접근
}
for (int i = 0; i < 1000; i++) {
    array[i] = i;  // array[0], array[1], ... 순차 접근
}

CPU가 데이터를 요청하면 캐시에 데이터가 있는지 먼저 확인한다. 있으면 히트로 처리하고, 없으면 메모리에서 데이터를 가져와 캐시에 복사한 뒤 CPU로 전달한다.

HitMissCPU 요청캐시에 존재?캐시에서 데이터 반환메모리에서 데이터 로드캐시에 복사CPU에 데이터 전달빠른 응답느린 응답

히트율(Hit Rate)은 요청한 데이터를 캐시에서 찾은 비율이며, 미스율(Miss Rate)은 1 - 히트율로 표현한다. 일반적인 히트율은 L1 캐시가 9599%, L2 캐시가 8095%, L3 캐시가 70~90%다. 그러나 히트율만으로 성능을 판단할 수는 없다. 캐시 접근 시간과 미스가 났을 때의 비용도 함께 봐야 한다.

평균 메모리 접근 시간(AMAT)은 Hit Time + (Miss Rate × Miss Penalty)로 계산한다. 히트 타임은 1-4 사이클, 미스 패널티는 50-200 사이클로 제시된다. 캐시 접근 시간과 메모리 접근 시간을 기준으로는 다음과 같이 쓸 수 있다.

히트율 = (캐시 히트 횟수) / (전체 메모리 접근 횟수)
미스율 = 1 - 히트율 = (캐시 미스 횟수) / (전체 메모리 접근 횟수)
AMAT = 캐시 접근 시간 + (미스율 × 메모리 접근 시간)

L1 접근 시간이 1 ns, 메모리 접근 시간이 100 ns, L1 히트율이 95%인 경우의 계산은 다음과 같다.

AMAT = 1ns + (0.05 × 100ns) = 1ns + 5ns = 6ns

이 조건에서는 캐시가 없을 때 100 ns이던 접근 시간이 캐시 사용 시 6 ns가 되며, 성능 향상은 16.7배다. 히트율이 높더라도 히트 타임이나 미스 패널티가 크면 AMAT가 기대만큼 낮아지지 않을 수 있으므로, 캐시 효과는 이 값을 함께 해석해야 한다.

데이터는 캐시 계층과 라인 단위로 관리된다

L1 캐시는 CPU 코어 내부에 있으며 가장 빠르고 작다(32-64KB). L2는 코어별 전용 캐시로 배치되고 크기와 속도가 그 중간에 놓인다(256KB-1MB). L3는 여러 코어가 공유하며 더 크고 느리다(4-32MB).

상위 캐시가 하위 캐시의 내용을 포함하는 구조는 포괄적(Inclusive) 방식이다. 반대로 각 레벨이 서로 다른 데이터를 저장하면 배타적(Exclusive) 방식이 된다.

캐시는 개별 바이트가 아니라 고정 크기 블록인 캐시 라인 단위로 데이터를 다룬다. 일반적으로 64바이트가 표준이지만 프로세서마다 다르다. 인접 데이터를 함께 불러와 공간적 지역성을 활용하고, 여러 워드를 한 번에 전송해 대역폭을 사용한다. 다만 서로 다른 데이터가 같은 라인에 있을 때 거짓 공유(False Sharing)가 문제가 될 수 있다.

한 캐시 엔트리에는 메모리 블록을 식별하는 태그(Tag), 캐시 위치를 정하는 인덱스(Index), 라인 내부 바이트 위치를 가리키는 오프셋(Offset)이 포함된다. 유효 비트(Valid Bit)는 데이터의 유효성을, 더티 비트(Dirty Bit)는 캐시 데이터가 수정됐는지를 표시한다.

블록을 배치하는 선택지

직접 매핑에서는 각 메모리 블록이 정해진 하나의 캐시 위치에만 들어간다. 캐시 인덱스는 (메모리 블록 주소) % (캐시 라인 수)로 정하며, 태그 비교는 한 번이면 된다.

메모리 블록 0, 8, 16...캐시 슬롯 0메모리 블록 1, 9, 17...캐시 슬롯 1메모리 블록 2, 10, 18...캐시 슬롯 2

이 방식은 인덱스로 위치를 결정하고 태그를 한 번 비교하므로 구조가 단순하며 하드웨어 복잡도도 낮다. 반면 서로 다른 블록이 같은 슬롯을 사용하면 계속 교체되는 충돌이 발생해 히트율이 제한될 수 있고, 캐시 스래싱을 일으킬 수 있다.

메모리 블록 0, 64, 128, ... → 캐시 라인 0
메모리 블록 1, 65, 129, ... → 캐시 라인 1
...

완전 연관 매핑(Fully Associative)은 모든 블록을 캐시의 어느 위치에나 저장할 수 있게 한다. 충돌을 최소화하고 히트율을 높일 수 있지만, 모든 태그를 병렬로 비교해야 하므로 복잡한 비교 회로가 필요하다. 캐시가 커질수록 검색도 느려진다. 이 때문에 TLB 같은 소형 캐시에 사용된다.

세트 연관 매핑(Set Associative)은 두 방식의 절충안이다. 캐시를 여러 세트로 나누고, 각 세트 안에 N개의 슬롯을 둔다. 인덱스로 세트를 고른 뒤 세트 내부의 태그를 비교하고, 미스가 발생하면 교체 정책을 적용한다. 집합 번호는 (메모리 블록 주소) % (집합 수)로 결정한다.

YesNo메모리 주소인덱스로 세트 선택세트 모든 태그 비교히트?데이터 반환교체 정책 적용

2-way, 4-way, 8-way 연관 구성이 일반적이며, 현대 CPU는 대부분 8-way 이상을 사용한다. L1 캐시는 8-way, L2 캐시는 8-16 way, L3 캐시는 16-24 way로 구성하는 경우가 일반적이다. 이는 히트율과 하드웨어 복잡도 사이의 균형을 맞추기 위한 선택이다.

교체와 쓰기에서 달라지는 비용

세트가 가득 찬 상태에서 새 블록을 넣으려면 기존 블록을 골라 교체해야 한다. LRU(Least Recently Used)는 가장 오래 사용하지 않은 블록을 교체한다. FIFO(First-In-First-Out)는 먼저 들어온 블록을, 랜덤(Random)은 무작위로 선택한 블록을 교체한다. LFU(Least Frequently Used)는 사용 빈도가 낮은 블록을 대상으로 하며, Pseudo-LRU는 하드웨어를 단순화한 LRU 근사 방식이다.

LRU는 각 블록의 최근 사용 시점을 기록하거나 세트 내 상대적 순서를 비트 벡터로 유지해 구현할 수 있다. 현대 프로세서는 대부분 LRU 변형을 사용한다. 지역성 활용에는 효과적이지만 연관도가 높아질수록 오버헤드와 복잡도가 증가한다.

쓰기 정책도 캐시와 메모리의 관계를 바꾼다. Write-Through는 CPU 쓰기를 캐시에 반영하는 동시에 메모리에도 기록한다.

CPU 쓰기캐시 업데이트메모리에도 즉시 쓰기일관성 보장

이 방식에서는 캐시와 메모리가 항상 같은 상태를 유지한다. 쓰기 버퍼를 이용하면 메모리 쓰기를 버퍼링해 CPU 대기를 줄일 수 있지만, 모든 쓰기가 메모리 버스를 사용하므로 버스 트래픽이 증가한다. 전원 차단 시에도 데이터가 보존된다는 특성이 있다.

Write-Back은 캐시만 먼저 업데이트하고, 해당 블록이 교체될 때 메모리에 반영한다. 더티 비트로 수정된 블록을 표시하며, 메모리 쓰기 횟수를 대폭 줄여 버스 효율을 높인다. 연속 쓰기 작업에는 유리하지만 일관성을 유지하는 메커니즘이 필요하다.

버퍼와 캐시가 맡는 역할

버퍼링은 속도가 다른 장치 사이의 데이터 전달을 중재한다. FIFO 큐를 이용해 순차적으로 처리하며, 키보드 입력 버퍼, 네트워크 송수신 버퍼, 디스크 I/O 버퍼가 해당한다. 보통 데이터는 한 번 사용된다.

캐싱의 관심사는 자주 사용되는 데이터를 빠르게 다시 읽는 데 있다. 연관 메모리와 LRU/LFU 교체 방식을 사용하며 CPU 캐시, 웹 브라우저 캐시, 데이터베이스 캐시가 사례다. 보관한 데이터는 반복적으로 재사용된다.

파일 시스템에서는 페이지 캐시로 자주 읽는 파일 데이터를 보관하고 I/O 버퍼로 디스크 쓰기를 배치 처리한다. 네트워크 스택은 소켓 버퍼에 송수신 데이터를 임시 저장하고 DNS 캐시에 도메인 이름 해석 결과를 보관한다. 두 방식은 경쟁 관계가 아니라 함께 쓰인다.

버퍼링은 CPU가 느린 I/O를 기다리지 않게 하고 I/O 장치에 연속적인 데이터 공급을 제공하며, 배치 처리로 처리량을 높인다. 캐싱은 평균 메모리 접근 시간을 낮추고, 히트 시 메모리 버스를 사용하지 않아 대역폭과 전력 소비를 아낀다.

다중 코어에서 캐시를 유지하는 방법

다중 코어 환경에서는 각 코어가 서로 다른 캐시에서 같은 데이터를 볼 수 있으므로 캐시 코히런스가 필요하다. MESI 프로토콜은 Modified, Exclusive, Shared, Invalid 상태를 관리한다. M(Modified)은 해당 캐시에만 있고 수정되어 메모리와 일치하지 않는 상태다. E(Exclusive)는 해당 캐시에만 있지만 메모리와 일치하며, S(Shared)는 여러 캐시가 복사본을 가진 상태다. I(Invalid)는 무효 데이터를 의미한다.

코어 0이 읽으면 E 상태가 되고, 코어 1이 같은 데이터를 읽으면 두 캐시는 S 상태가 된다. 이후 코어 0이 쓰면 코어 0은 M, 코어 1은 I로 전환한다. 코어 1이 다시 읽을 때는 코어 0에서 데이터를 가져오거나 메모리 업데이트 후 데이터를 받는다.

스누핑(Snooping)은 캐시 간 버스 감시로 일관성을 유지하며, 모든 캐시가 버스 트래픽을 감시해 다른 캐시의 쓰기를 감지하고 자신의 복사본을 무효화하는 방식이다. 디렉터리 기반 방식은 공유 상태 정보를 중앙에서 관리하며, 대규모 시스템에서 스누핑보다 스케일러빌리티가 좋고 버스 트래픽을 줄인다.

L3 캐시를 모든 코어가 공유하면 일관성 문제를 줄이고 캐시 공간을 효율적으로 활용할 수 있다. L1·L2에서 미스가 나도 L3에서 데이터를 얻을 수 있어 코어 간 통신과 메모리 트래픽 측면에서 이점이 있다. 반면 L3 접근 경쟁, 캐시 파티셔닝, 복잡한 관리가 함께 따라온다.

일관성 유지는 모든 코어가 일관된 데이터를 보게 하지만 오버헤드를 수반한다. 프리페칭(Prefetching)은 앞으로 접근할 데이터를 미리 적재하고, 넌블로킹 캐시(Non-blocking Cache)는 미스가 발생한 동안에도 히트를 처리한다. 크리티컬 워드 퍼스트는 필요한 워드를 먼저 전송하며, 멀티뱅크 캐시는 병렬 접근으로 대역폭을 높인다. 희생 캐시(Victim Cache)는 교체된 블록을 임시로 저장한다.

캐시 메모리컴퓨터구조메모리 계층캐시 매핑캐시 일관성