메모리 계층 구조
메모리 계층 구조(Memory Hierarchy)는 빠를수록 용량이 작고 비싼 경향이 있는 저장 장치들을 계층으로 배치한 구조입니다. CPU에 가까울수록 대체로 빠르고 비싸며 용량이 작고, 멀어질수록 대체로 느리지만 저렴하고 용량이 큽니다.
계층 1
레지스터 (Register)
CPU 내부에 위치한 가장 빠른 저장 공간입니다. 접근 시간 1ns 미만, 용량은 수십~수백 바이트 수준입니다.
계층 2
L1 / L2 / L3 캐시
CPU와 RAM 사이의 고속 버퍼입니다. 대략 L1은 코어당 수십 KB, L2는 수백 KB~수 MB, L3는 수 MB~수십 MB 규모이며, 접근 시간은 L1이 약 1ns, L3가 십수~수십 ns입니다(세대와 제품마다 다릅니다).
계층 3
메인 메모리 (RAM)
프로그램 실행 시 데이터를 올려두는 주기억장치입니다. 수 GB~수십 GB 용량, 접근 시간 대략 60~100ns(제품마다 다름)입니다.
계층 4
보조 기억 장치 (SSD/HDD)
영구 저장 장치입니다. SSD는 수십~수백 μs, HDD는 수 ms 접근 시간이며 수백 GB~수 TB 용량을 제공합니다.
핵심 원리
지역성 원리
시간 지역성(최근 사용 데이터 재사용)과 공간 지역성(인접 주소 데이터 접근)을 이용해 상위 계층 적중률을 높입니다.
핵심 원리
계층 간 데이터 이동
상위 계층에 데이터가 없으면(Miss) 하위 계층에서 블록 단위로 가져옵니다. 히트율이 높을수록 전체 성능이 향상됩니다.
계층이 생긴 이유
CPU 속도는 DRAM 접근 속도보다 훨씬 빠르게 좋아져 왔습니다. 1995년 Wulf와 McKee는 이 격차가 계속 벌어지면 성능을 정하는 것이 CPU가 아니라 메모리 접근 속도가 될 것이라며 '메모리 벽(memory wall)'이라는 말을 쓴 글을 발표했습니다. 빠른 저장 장치는 작고 비싸고 느린 장치는 크고 싸기 때문에 한 종류만으로는 속도, 용량, 가격을 함께 만족할 수 없습니다. 그래서 작고 빠른 저장소를 CPU 가까이에 두고, 프로그램이 같은 데이터나 가까운 데이터를 다시 쓴다는 지역성을 이용해 대부분의 접근을 위쪽 계층에서 끝내는 구조가 표준이 되었습니다.
평균 접근 시간을 숫자로 보면
위 공식에 설명용 값을 넣어 보겠습니다. 캐시 접근 시간을 1ns, 메모리 접근 시간을 100ns라고 가정합니다.
자주 하는 오해
면접에서 이렇게 물어요
면접 질문
지역성은 캐시와 어떻게 연결되나요?
시간 지역성은 최근에 쓴 데이터를 곧 다시 쓴다는 성질이라 캐시에 남겨 두면 이득이 됩니다. 공간 지역성은 가까운 주소를 이어서 쓴다는 성질이라, 캐시는 미스가 나면 한 바이트가 아니라 캐시 라인(x86에서는 보통 64바이트) 단위로 가져옵니다.
면접 질문
2차원 배열을 행 순서와 열 순서로 순회하면 속도가 왜 다른가요?
C에서 2차원 배열은 행 단위로 메모리에 이어서 저장됩니다. Java의 2차원 배열은 배열의 배열이라 행마다 별도의 배열이고, 한 행 안의 원소는 기본형 배열이면 보통(JVM 구현상) 연속해서 놓입니다. 행 순서로 순회하면 한 캐시 라인에 든 원소를 연달아 쓰지만, 행이 충분히 길고 배열이 캐시보다 크면 열 순서로 순회할 때 원소마다 다른 라인을 가져와서 미스가 늘어납니다. 결과는 같지만 시간은 다릅니다.
면접 질문
레지스터와 캐시는 무엇이 다른가요?
레지스터는 명령어가 이름으로 직접 지정하는 CPU 내부 저장소입니다. 캐시는 메모리 주소를 기준으로 하드웨어가 투명하게 관리하는 메모리의 사본이라, 프로그램이 캐시를 이름으로 지정하지 않습니다.
면접 질문
평균 메모리 접근 시간을 줄이려면 어떻게 하나요?
히트율을 높이거나(지역성 좋은 코드, 캐시 크기와 구성), 미스 때의 비용을 줄이거나(다단계 캐시, 프리페치), 히트 시간을 줄입니다(작고 단순한 L1). 세 요소가 서로 맞물려 있어서 하나를 개선하면 다른 하나가 나빠지기도 합니다.
실제 시스템에서는
lscpu -C의 ONE-SIZE 열에서 볼 수 있습니다.
/sys/devices/system/cpu/cpu0/cache/index0/, index1/ 같은 하위 디렉터리 아래의 level, type, size, coherency_line_size 파일로 캐시 계층과 캐시 라인 크기를 읽을 수 있습니다.
free 명령의 buff/cache 항목에는 이 페이지 캐시가 포함됩니다.
함께 보면 좋은 토픽
참고 자료
C — 메모리 계층별 접근 시간 시뮬레이션
// 메모리 계층별 접근 시간 (나노초 단위) #define T_REGISTER 1 // 레지스터: ~1 ns #define T_L1 4 // L1 캐시: 4 ns #define T_L2 12 // L2 캐시: 12 ns #define T_L3 40 // L3 캐시: 40 ns #define T_RAM 100 // RAM: 100 ns #define T_SSD 100000 // SSD: 100,000 ns (100 μs) // 히트율 기반 평균 접근 시간 계산 (표준 계층적 공식) // AMAT = T_L1 + MissRate_L1 × (T_L2 + MissRate_L2 × (T_L3 + MissRate_L3 × T_RAM)) // L2 히트 시에도 L1 조회(미스) 비용을 함께 지불해야 하므로 누적 합산한다. double calc_amat(double h_l1, double h_l2, double h_l3) { // L3 미스 시 페널티: L3 접근 + RAM 접근 double miss_l3_penalty = T_L3 + (1.0 - h_l3) * T_RAM; // L2 미스 시 페널티: L2 접근 + L3 탐색 비용 double miss_l2_penalty = T_L2 + (1.0 - h_l2) * miss_l3_penalty; // AMAT: L1 접근 + L1 미스 시 추가 페널티 double amat = T_L1 + (1.0 - h_l1) * miss_l2_penalty; return amat; } // 예시: L1=90%, L2=80%, L3=90% 히트율 // AMAT = 4 + 0.1 × (12 + 0.2 × (40 + 0.1 × 100)) // = 4 + 0.1 × (12 + 0.2 × 50) // = 4 + 0.1 × 22 = 6.2 ns
AMAT(Average Memory Access Time)은 계층별 히트율과 접근 시간으로 계산합니다. L1 히트율이 90%일 때 10%의 미스가 하위 계층으로 전파되므로 전체 계층의 히트율을 높이는 것이 성능 향상의 핵심입니다.
Java — 메모리 계층 접근 패턴 시뮬레이터
// 메모리 계층 시뮬레이터 — 지역성 원리 확인 public class MemoryHierarchySim { private static final int L1_SIZE = 128; // 128 KB private static final int L2_SIZE = 1024; // 1 MB private static final int L3_SIZE = 8192; // 8 MB private long l1Hits, l2Hits, l3Hits, ramHits; private long totalAccess; /** 주소 접근 시뮬레이션 */ public long access(int address) { totalAccess++; // 간단한 직접 매핑 시뮬레이션 if (inCache(address, L1_SIZE)) { l1Hits++; return 4; // L1 접근: 4ns } if (inCache(address, L2_SIZE)) { l2Hits++; return 12; // L2 접근: 12ns } if (inCache(address, L3_SIZE)) { l3Hits++; return 40; // L3 접근: 40ns } ramHits++; return 100; // RAM 접근: 100ns } private boolean inCache(int addr, int size) { return (addr % (size * 1024)) < (size * 512); } /** 히트율 및 평균 접근 시간 출력 */ public void printStats() { double amat = (l1Hits*4.0 + l2Hits*12.0 + l3Hits*40.0 + ramHits*100.0) / totalAccess; System.out.printf("L1 히트율: %.1f%%%n", l1Hits*100.0/totalAccess); System.out.printf("AMAT: %.2f ns%n", amat); } }
시간 지역성이 높은 워크로드(같은 주소 반복 접근)는 L1 히트율이 높아 AMAT가 4ns에 근접합니다. 반면 순차 대용량 배열 접근(공간 지역성만 존재)은 캐시 미스가 많아 AMAT가 RAM 접근 시간인 100ns에 가까워집니다.
Q1
메모리 계층 구조에서 레지스터에 가까울수록 일반적으로 어떤 특징을 가지나요?
Q2
메모리 계층 구조를 두는 근본적인 이유는?
Q3
지역성(locality)의 원리가 메모리 계층에서 중요한 이유는?