Skip to content
난이도 면접 빈출문제은행 4

메모리와 캐시 2 ​

메모리와 캐시에서 본 캐시(cache)가 실제로 어떻게 동작하는지 이어서 다룬다. 고친 값을 메모리에 언제 쓸지, 메모리의 데이터를 캐시의 어느 칸에 둘지를 보고, 작은 캐시가 잘 통하는 이유인 지역성(locality)을 설명한다. 마지막으로 코어(core)마다 캐시를 따로 둘 때 생기는 문제를 본다.

캐시에 쓸 때: 즉시 쓰기와 나중 쓰기 ​

CPU가 캐시에 올라온 값을 고치면, 메인 메모리에는 언제 반영할지 정해야 한다. 방식은 두 가지다.

  • 즉시 쓰기(write-through): 캐시와 메모리에 동시에 쓴다. 두 곳이 늘 같아 단순하지만, 쓸 때마다 느린 메모리까지 가야 한다.
  • 나중 쓰기(write-back): 캐시에만 쓰고, 그 값이 든 캐시 라인(cache line)에 바뀌었다는 표시(dirty bit)를 해 둔다. 그 라인이 캐시에서 쫓겨날 때 한 번에 메모리에 쓴다. 같은 값을 여러 번 고쳐도 메모리 쓰기는 한 번이라 빠르지만, 메모리와 캐시가 잠시 어긋나 관리가 복잡하다.

요즘 CPU는 대부분 나중 쓰기를 쓴다. 데이터베이스의 버퍼 풀(buffer pool)이 바뀐 페이지(디스크와 한 번에 주고받는 데이터 묶음)를 바로 디스크에 쓰지 않고 모아 두었다가 쓰는 것도 같은 생각이다.

캐시에 어디 둘까: 사상 방식 ​

메모리를 캐시 라인과 같은 크기로 나눈 한 덩어리를 블록(block)이라 한다. 메모리는 캐시보다 훨씬 크므로, 어느 블록을 캐시의 어느 칸에 둘지 규칙이 필요하다. 이 규칙을 사상(mapping) 방식이라 하고, 정보처리기사에 자주 나온다.

방식규칙장점단점
직접 사상(direct mapped)블록마다 들어갈 칸이 하나로 정해져 있다찾기가 빠르고 회로가 단순하다같은 칸을 쓰는 블록끼리 서로 계속 쫓아낸다
완전 연관 사상(fully associative)아무 칸에나 둘 수 있다빈칸이 있으면 충돌이 없다모든 칸을 뒤져야 해서 회로가 비싸다
집합 연관 사상(set associative)정해진 집합 안의 몇 칸 중 아무 데나 둔다둘의 절충집합이 가득 차면 그 안에서 교체한다

실제 CPU 캐시는 대부분 집합 연관 사상을 쓴다. 한 집합에 칸이 8개면 "8웨이(8-way)"라고 부른다.

지역성 ​

캐시는 크기가 메모리보다 훨씬 작은데도 적중률(hit ratio)이 높다. 그 근거가 지역성이다. 지역성은 프로그램이 최근에 쓴 데이터나 그 근처 데이터를 곧 다시 쓰는 경향을 말한다.

  • 시간 지역성(temporal locality): 방금 쓴 데이터를 곧 다시 쓴다. 반복문의 카운터 변수나 자주 호출하는 함수가 예다. 캐시는 최근에 쓴 것을 남겨 두는 방식으로 이 경향을 이용한다.
  • 공간 지역성(spatial locality): 방금 쓴 주소의 근처를 곧 쓴다. 배열(array)을 앞에서부터 차례로 읽는 경우가 예다. 캐시 라인 단위로 옆 데이터를 함께 가져오는 방식이 이 경향을 이용한다.

실무에서 가장 흔한 예는 2차원 배열을 도는 순서다. C처럼 2차원 배열을 행 단위로 이어 붙여 저장하는 언어에서, 행을 따라 도는 반복문은 캐시 라인을 끝까지 다 쓰고 다음 라인으로 넘어간다. 열을 따라 돌면 매번 다른 행, 즉 멀리 떨어진 주소로 뛰므로 가져온 라인의 대부분을 버리게 되고 캐시 미스(cache miss)가 크게 늘어난다. 원소 수와 계산량이 같아도 순서 하나로 속도가 몇 배 차이 날 수 있다.

c
// 빠름: 행을 따라 옆 주소를 차례로 읽는다
for (int i = 0; i < N; i++)
    for (int j = 0; j < N; j++) sum += a[i][j];

// 느림: 열을 따라 한 행 길이만큼씩 건너뛴다
for (int j = 0; j < N; j++)
    for (int i = 0; i < N; i++) sum += a[i][j];

데이터베이스가 디스크 페이지를 메모리에 모아 두는 버퍼 풀도 같은 원리를 한 층 아래에서 쓰는 것이다.

캐시 일관성 ​

멀티코어(multi-core) CPU에서는 코어마다 가장 가깝고 빠른 1단계(L1)·2단계(L2) 캐시가 따로 있다. 두 코어가 같은 메모리 주소를 각자 캐시에 올린 뒤 한쪽이 값을 바꾸면, 다른 코어는 옛 값을 계속 읽을 수 있다. 이 어긋남을 막아 모든 코어가 같은 주소에 대해 같은 값을 보게 하는 것을 캐시 일관성(cache coherence)이라 한다.

하드웨어는 캐시끼리 변경을 서로 알리는 규칙인 일관성 규약(coherence protocol)으로 이를 처리한다. 대표적인 MESI 규약은 네 상태의 머리글자를 딴 이름으로, 캐시 라인마다 수정됨(Modified)·독점(Exclusive)·공유(Shared)·무효(Invalid) 상태를 붙여 둔다. 한 코어가 라인을 고치면 다른 코어의 같은 라인은 무효가 되고, 그 코어는 다음에 새 값을 다시 가져온다. 이 때문에 서로 다른 변수라도 같은 캐시 라인에 있으면 두 코어가 번갈아 고칠 때마다 라인이 무효가 되는 거짓 공유(false sharing)가 생겨 성능이 떨어진다.

면접 포인트 ​

  • "지역성 두 가지와 예는?" → 시간(반복문 변수), 공간(배열 순차 접근·캐시 라인).
  • "2차원 배열을 열 순서로 돌면 왜 느린가요?" → 공간 지역성이 깨져 캐시 미스가 늘어난다.
  • "즉시 쓰기와 나중 쓰기 차이는?" → 매번 메모리까지(단순·느림) vs 쫓겨날 때 한 번(빠름·관리 복잡).
  • "캐시 사상 방식 세 가지는?" → 직접(칸 고정)·완전 연관(아무 칸)·집합 연관(절충, 실제 CPU).
  • "캐시 일관성이 왜 필요한가요?" → 코어별 캐시가 같은 주소의 다른 값을 들고 있을 수 있어서. MESI, 거짓 공유.

관련 문서