1. 캐시의 필요성: 속도 차이와 지역성
컴퓨터 시스템에서 CPU의 데이터 처리 속도는 메모리(RAM)의 데이터 접근 속도보다 훨씬 빠르다. 기술의 발전으로 CPU의 클럭 속도는 기하급수적으로 증가했지만, DRAM을 기반으로 하는 메인 메모리의 속도는 그에 미치지 못했다. 이로 인해 CPU가 아무리 빨라도 메모리에서 데이터를 가져오는 것을 기다려야 하는 병목 현상(Bottleneck)이 발생하며, 이를 메모리 장벽(Memory Wall)이라고도 부른다.
캐시 메모리는 이러한 CPU와 메인 메모리 간의 속도 차이를 완화하기 위해 등장한 고속의 임시 저장 공간이다.
캐시가 효율적으로 동작할 수 있는 근거는 프로그램이 데이터에 접근할 때 나타나는 지역성의 원리(Principle of Locality) 때문이다.
- 시간적 지역성 (Temporal Locality): 최근에 접근했던 데이터는 가까운 미래에 다시 접근될 가능성이 높다. (예: 반복문 내의 변수, 함수 내의 지역 변수)
- 공간적 지역성 (Spatial Locality): 특정 데이터에 접근했다면, 그 주변의 데이터에도 곧 접근할 가능성이 높다. (예: 배열의 원소를 순차적으로 접근, 순차적인 코드 실행)
캐시는 이러한 지역성 원리를 활용하여, CPU가 요청할 것으로 예상되는 데이터를 메인 메모리에서 미리 가져와 저장해 둔다. CPU가 필요한 데이터가 캐시에 존재하면(이를 캐시 히트(Cache Hit)라고 함), 비싼 비용의 메인 메모리 접근 없이 데이터를 즉시 사용할 수 있어 시스템의 전반적인 성능이 크게 향상된다.
2. 캐시의 기본 단위: 캐시 라인 (Cache Line)
캐시는 메인 메모리와 데이터를 주고받을 때 바이트(Byte) 단위가 아닌, 정해진 크기의 덩어리(Block) 단위로 데이터를 전송한다. 이 단위를 캐시 라인(Cache Line) 또는 캐시 블록(Cache Block)이라고 한다.
- 현대 CPU의 캐시 라인 크기는 보통 64바이트(Bytes)다.
- CPU가 특정 메모리 주소(예:
0x1000)의 데이터를 요청했는데 캐시에 없다면(캐시 미스), 캐시는 메인 메모리에서 해당 주소를 포함하는 64바이트 크기의 블록(예:0x1000~0x103F) 전체를 캐시 라인에 채운다. - 이는 공간적 지역성에 따라
0x1000번지 다음에는0x1004번지(int형 변수일 경우)에 접근할 확률이 높기 때문에, 한 번의 메모리 접근으로 여러 번의 미래 접근을 대비하는 효율적인 전략이다.
하나의 캐시 라인은 다음과 같은 요소로 구성된다.
- 데이터 블록 (Data Block): 메인 메모리에서 가져온 실제 데이터 (e.g., 64 Bytes).
- 태그 (Tag): 메인 메모리 주소의 일부 정보를 저장하여, 캐시 라인에 있는 데이터가 어떤 메모리 주소의 데이터인지 식별하는 데 사용된다.
- 플래그 비트 (Flag Bits):
- Valid Bit: 캐시 라인에 유효한 데이터가 들어있는지 여부를 나타낸다. (1: 유효, 0: 무효)
- Dirty Bit: 캐시의 데이터가 메인 메모리의 데이터와 달라졌는지(수정되었는지) 여부를 나타낸다. (1: Dirty, 0: Clean). 이 비트는 'Write-Back' 정책에서 사용된다.
3. 캐시의 동작 원리: 매핑(Mapping) 방식
캐시 매핑은 메인 메모리의 블록을 캐시의 어느 라인에 배치할지를 결정하는 규칙이다. 대표적으로 세 가지 방식이 있다.
3.1. 직접 매핑 (Direct Mapped Cache)
메모리 블록이 캐시의 특정 한 위치에만 매핑될 수 있는 가장 간단한 방식이다.
- 계산 방식:
(메모리 주소) % (캐시 라인의 수) - 장점: 구현이 매우 간단하고 하드웨어 비용이 저렴하다. 특정 라인만 확인하면 되므로 히트/미스 판단이 빠르다.
- 단점: 서로 다른 메모리 주소가 같은 캐시 라인으로 매핑될 경우, 두 데이터를 동시에 사용하려 할 때 지속적으로 캐시 미스가 발생하며 교체(Thrashing)가 일어난다. 이를 충돌 미스(Conflict Miss)라고 한다.
3.2. 완전 연관 매핑 (Fully Associative Cache)
메모리 블록이 캐시의 어느 라인이든 자유롭게 위치할 수 있는 가장 유연한 방식이다.
- 동작 방식: 데이터를 찾기 위해 캐시의 모든 라인을 동시에 검색해야 한다.
- 장점: 충돌 미스가 발생하지 않아 캐시 공간을 매우 효율적으로 사용할 수 있다.
- 단점: 모든 태그를 동시에 비교하기 위한 CAM(Content Addressable Memory)이라는 고가의 비교 회로가 필요하다. 회로가 복잡하고 비싸며, 검색 시간이 오래 걸려 대용량 캐시에는 부적합하다.
3.3. 집합 연관 매핑 (Set-Associative Cache)
직접 매핑과 완전 연관 매핑의 장점을 절충한 방식으로, 현대 CPU에서 가장 널리 사용된다.
- 동작 방식: 캐시를 여러 개의 집합(Set)으로 나눈다. 메모리 블록은 정해진 하나의 집합 내에서는 어느 라인이든 자유롭게 위치할 수 있다.
- 계산 방식:
(메모리 주소) % (집합의 수) N-way Set-Associative는 하나의 집합이 N개의 캐시 라인으로 구성됨을 의미한다.- 장점: 직접 매핑에 비해 충돌 미스가 현저히 줄어들면서, 완전 연관 매핑만큼의 복잡한 하드웨어를 요구하지 않는다.
- 단점: 직접 매핑보다는 회로가 복잡하다.
| 매핑 방식 | 장점 | 단점 |
|---|---|---|
| 직접 매핑 | 간단, 저렴, 빠름 | 충돌 미스 확률 높음 |
| 완전 연관 | 충돌 미스 없음, 공간 효율성 높음 | 매우 비싸고 복잡함, 검색 속도 느림 |
| 집합 연관 | 성능과 비용의 적절한 균형 | 직접 매핑보다 복잡함 |
4. 캐시 정책
4.1. 쓰기 정책 (Write Policy)
CPU가 데이터를 수정했을 때, 이 변경사항을 캐시와 메인 메모리에 어떻게 반영할지를 결정하는 정책이다.
- Write-Through (즉시 쓰기): 데이터를 캐시와 메인 메모리에 동시에 쓴다.
- 장점: 구조가 간단하고, 캐시와 메모리의 데이터가 항상 일치하여 데이터 일관성을 유지하기 쉽다.
- 단점: 매번 쓰기 동작마다 느린 메인 메모리에 접근해야 하므로 쓰기 속도가 느리다.
- Write-Back (지연 쓰기): 데이터는 우선 캐시에만 쓰고, 해당 캐시 라인에 '수정됨'을 의미하는 Dirty Bit를 1로 설정한다. 이 라인이 캐시에서 빠지게 될 때(교체될 때) Dirty Bit가 1이면, 그때 변경사항을 메인 메모리에 한 번에 반영한다.
- 장점: 동일한 데이터에 대한 반복적인 쓰기 작업이 메모리 접근 없이 캐시 내에서만 빠르게 이루어져 쓰기 성능이 크게 향상된다.
- 단점: 구조가 복잡하며, 특정 시점에는 캐시와 메모리의 데이터가 불일치할 수 있다. 다중 코어 환경에서는 데이터 일관성 문제가 발생할 수 있어 이를 해결하기 위한 추가적인 프로토콜(e.g., MESI)이 필요하다.
4.2. 교체 정책 (Replacement Policy)
캐시가 가득 찬 상태에서 새로운 데이터를 가져와야 할 때, 어떤 캐시 라인을 비울(교체할)지를 결정하는 정책이다. (주로 집합 연관 매핑에서 사용)
- LRU (Least Recently Used): 가장 오랫동안 사용되지 않은 라인을 교체한다. 시간적 지역성을 가장 잘 활용하는 효율적인 알고리즘이지만, 모든 라인의 최근 사용 시간을 추적해야 하므로 구현이 복잡하다.
- FIFO (First-In, First-Out): 가장 먼저 들어온 라인을 교체한다. 구현은 간단하지만, 자주 사용되는 데이터라도 먼저 들어왔다는 이유로 교체될 수 있어 효율이 떨어질 수 있다.
- LFU (Least Frequently Used): 참조 횟수가 가장 적은 라인을 교체한다. 구현이 복잡하고, 초기에 많이 사용되다 이후 사용되지 않는 데이터가 계속 캐시에 남아있을 수 있다.
- Random: 무작위로 하나의 라인을 선택하여 교체한다. 구현은 매우 간단하지만 성능을 예측하기 어렵다.
5. 다중 레벨 캐시 (Multi-Level Caches)
현대의 CPU는 속도와 용량이 다른 여러 단계의 캐시를 두는 다중 레벨 캐시 구조를 가진다.
- L1 캐시: CPU 코어 내부에 위치하며, 가장 빠르고 용량이 가장 작다(수십 KB). 접근 속도가 거의 레지스터 수준이다. 보통 명령어만 저장하는 L1-I(Instruction) 캐시와 데이터만 저장하는 L1-D(Data) 캐시로 분리되어 있다.
- L2 캐시: L1 캐시보다 크고(수백 KB ~ 수 MB), 조금 더 느리다. 과거에는 CPU 칩 외부에 있었으나, 현재는 코어 내부 또는 바로 옆에 위치한다.
- L3 캐시: 여러 CPU 코어가 공유하는 가장 크고(수 MB ~ 수십 MB) 느린 캐시다. L2 캐시 미스 시 다음으로 접근하는 곳이며, 코어 간 데이터 공유를 원활하게 하는 역할도 한다.
동작 계층 구조 예시 (int x = array[10]; 실행 시)
- CPU가
array[10]의 메모리 주소를 계산하여 데이터 요청. - L1 캐시 확인: 데이터가 있는가? (L1 Hit) -> 즉시 CPU 레지스터로 데이터 전송. (가장 좋은 시나리오)
- L1 미스: L1에 데이터가 없음. L2 캐시 확인: 데이터가 있는가? (L2 Hit) -> L2에서 해당 캐시 라인을 L1으로 복사하고, CPU로 데이터 전송.
- L2 미스: L2에도 없음. L3 캐시 확인: 데이터가 있는가? (L3 Hit) -> L3에서 해당 캐시 라인을 L2, L1으로 순차적으로 복사하고, CPU로 데이터 전송.
- L3 미스 (최종 캐시 미스): 모든 캐시에 데이터가 없음.
a. CPU는 메모리 컨트롤러를 통해 메인 메모리(DRAM)에 데이터 요청.
b. 메인 메모리에서array[10]을 포함하는 전체 캐시 라인(64 Bytes)을 가져온다.
c. 이 데이터를 L3, L2, L1 캐시에 순서대로 채운다. (이 과정에서 필요시 교체 정책에 따라 기존 라인 제거)
d. 최종적으로 CPU 레지스터로 원하는 데이터를 전송한다.
6. 요약
캐시 메모리는 CPU와 메인 메모리(RAM) 사이의 속도 차이를 해소하기 위해 존재하는 임시 저장소입니다. CPU는 매우 빠르지만 메모리는 상대적으로 느려서, CPU가 메모리의 데이터를 기다리며 성능이 저하되는 병목 현상이 발생합니다. 캐시는 이 중간에서 CPU가 자주 사용할 것으로 예상되는 데이터를 미리 저장해두어, CPU가 메모리까지 가지 않고도 데이터를 빠르게 가져갈 수 있도록 돕는 역할을 합니다.
캐시가 효율적인 이유는 데이터 접근의 '지역성' 원리 덕분입니다. 즉, '최근에 쓴 데이터는 다시 쓸 확률이 높고(시간적 지역성)', '하나의 데이터에 접근하면 그 주변 데이터도 접근할 확률이 높다(공간적 지역성)'는 특징을 활용하는 것입니다.
캐시의 동작은 CPU가 데이터를 요청할 때 시작됩니다. 먼저 가장 가까운 L1 캐시를 확인하고, 없으면 L2, L3 캐시 순으로 찾아봅니다. 데이터가 캐시에 있으면 '캐시 히트'라고 하며 매우 빠르게 처리가 끝납니다. 만약 모든 캐시에 데이터가 없으면 '캐시 미스'라고 하며, 이때 메인 메모리에서 해당 데이터를 포함한 데이터 덩어리, 즉 '캐시 라인' 전체를 캐시로 가져옵니다. 이 과정에서 캐시가 꽉 차 있다면, LRU와 같은 교체 정책에 따라 기존 데이터 중 하나를 내보내고 새로운 데이터를 저장합니다. 이런 계층적인 구조를 통해 캐시는 시스템의 전반적인 성능을 크게 향상시킵니다.
'Computer Science' 카테고리의 다른 글
| 프로세스와 스레드 Process and Thread (0) | 2025.11.19 |
|---|---|
| CPU 스케줄링 CPU Scheduling (0) | 2025.11.19 |
| CPU (0) | 2025.11.19 |
| 가상 메모리 Virtual Memory (0) | 2025.11.17 |
| 메모리 구조 (0) | 2025.11.17 |
