
저번에 포스팅한 기본 컴퓨터 시스템구조를 통해서 메모리는 용량이 클수록 느리고, 작을수록 빠르다는 물리적 한계가 있다는 사실을 알고 있었어요.
그래서 캐시메모리가 중요하다는 사실을 인지하고 있었는데
이번 6장 "메모리 계층구조"를 통해서 메모리를 어떻게 관리하고 다루는지 공부해 보자구요.
지금까지 우리가 CSAPP 1~5장의 내용들에 대해서는
전통적인 모델로 CPU와 메모리만을 고려해서 작성되었어요. 메모리를 선형적인 배열로 보고 일정 시간 내에 접근 가능한것으로 가정해서 진행했는데
실제로 우리는 메모리 시스템이 계층구조의 다양한 캐시메모리를 사용하고 있다는 것을 알고 있죠?
만약 우리가 시스템이 어떻게 데이터를 메모리 계층구조에서 위아래로 이동시키는지 이해한다면
데이터를 계층 상위에 저장시켜서 CPU가 보다 빨리 접근할 수 있도록 코드를 작성할 수 있지 않을까요?
사실 이건 "지역성"이라고 부르는 개념이예요.
지역성이 뭔지는 이후 장에서도 살펴볼게요.
그래서 이번장에서는!! 기본 저장장치 기술들을 살펴봅니다.
그리고 얘네가 어떻게 계층구조로 구성되는지 설명할거예요.
특히 CPU와 메인메모리 사이에 준비 영역으로 사용되는 캐시에 집중할 겁니다.
컴퓨터 기술중에 대부분의 성공은 저장장치 기술의 엄청난 발전에 기인해요.
초기 컴퓨터는 그냥 몇 KB 의 데이터만 저장할 수 있었지만
지금은 거의 300,000배가 넘고
매 2년마다 2배씩 메모리 저장용량이 증가하고 있어요.
랜덤 접근 메모리 ( RAM - Random Access Memory ) 는 두 종류를 가져요.
정적 램과 동적 램.
정적램 SRAM 은 캐시메모리로 사용되고 CPU 칩 내부 또는 외부에 장착되요.
동적램 DRAM 은 메인메모리와 그래픽 시스템의 프레임버퍼로 사용돼요.
일반적으로는
를 갖게돼요.
이 이후의 내용을 읽기전에, SRAM은 캐시메모리, DRAM 은 메인메모리 그래픽 시스템의 버퍼로 사용된다고만 알고 있어도 1-1장은 충분해요.
SRAM 은 각 비트를 이중안정 메모리 셀에 저장해요.
이게 뭐냐면. 우리가 비트에 0 혹은 1이라는 값을 저장했다고 해요.
이건 전기가 계속 공급되고 있는 상태여야 유지돼요. DRAM 은 64MS 초마다 전기적 충전을 해줘서 값을 유지하는데 이걸 Refresh라고 해요.
하지만 SRAM 처럼 이중안정 메모리 셀에 저장하면 전기적 충전 없이도 값이 유지돼요.
그래서 빠르고 신뢰성이 높아요.
근데 비싸요 ㅋ

보통은 이 이중안정 메모리를 진자에 비유하는데요.
끝에 무거운 추를 메단 진자가 있으면 왼쪽혹은 오른쪽으로 기울어져야 움직이지 않는 안정된 상태잖아요?
가운데에 서있으면 왼쪽으로 쓰러질지 오른쪽으로 쓰러질지 모르는 불안정한 상태예요.
그래서 SRAM 의 특징 하나 더는 살짝 충격이 있어도 원래 상태로 자동 복귀돼요.
전기적 잡음이나 빛 같은 외란에 대해 민감하지 않아요.
근데 DRAM 보다 트랜지스터 더 많이 쓰고 , 그래서 밀도가 더 낮고, 그래서 비싸고, 전기가 많이들어요.
DRAM 은 각 비트를 전하로 캐패시터 라는 곳에 저장해요.
난 비전공자니까 캐패시터가 뭔진 몰라요.
근데 매우 작고, 각 셀은 캐패시터 하나와 접근 트랜지스터 하나로 구성돼요.
SRAM 과 달리 외란에 매우 민감하고, 캐패시터 전하가 달라지면 다시 회복할 수 없어요.
여러가지 원인의 전류누수가 dram 셀이 10~100밀리초 사이에 전하를 달라지게 해요.
그래서 주기적으로 메모리의 모든 비트를 읽었다가 다시 써주는 방식으로 Refresh해야해요.
DRAM 의 셀들은 d 슈퍼셀단위로 나눠지고, 또 각각의 d 슈퍼셀은 w DRAM셀들로 이뤄져요.
총 dxw DRAM비트의 정보를 저장해요.
이 슈퍼셀들은 Row와 Columns 로 이루어진 직사각형 배열로 구성되어 있어요.
그래서 슈퍼셀 [ i ][ j ] 를 읽으려면, 메모리컨트롤러가 행주소 i를 보내고 j를 보내요.
i 를 읽는 것을 RAS ( row access storbe ) 라고 하구요.
j 를 읽는 것을 CAS ( column access storbe ) 라고 해요.
그리고 i를 먼저 그림의 internal row buffer 에 복사하고.
그 internal row buffer 에서 j를 읽어요.

왜 이렇게 만들었을까요?
바로 주소핀수를 줄이기 위해서예요. 주소핀 수를 줄이면 뭐가 좋을까요?
컴퓨터 조립해보면 알겠지만 RAM 은 메인보드에 꽂을 수 있게 되어있어요.
이런식으로 각각의 RAM 들은 하나하나 모듈로 패키징되어서 출시돼요.
DRAM 메모리는 여러 종류가 있고 규칙적으로 시장에 새로운 DRAM이 나오는데요.
그 종류를 알아볼게요.
| 기술 | 특징 |
|---|---|
| FPM DRAM | 같은 행에서 연속 접근 빠름 |
| EDO DRAM | FPM보다 더 빠름 |
| SDRAM | 클럭 기반 동기식 접근 |
| DDR SDRAM | 클럭의 양쪽 엣지를 활용하여 속도 2배 |
| VRAM | 그래픽 전용, 읽기와 쓰기 동시 가능 |
뭐 이런 기술들이 있다 정도만 인지하고 넘어가셔도 될 것 같아요.
DRAM과 SRAM은 전원이 꺼지면 정보도 사라져요.
그래서 휘발성 메모리입니다.
반면, 비 휘발성 메모리는 전원이 꺼져도 값이 유지돼요.
다양한 비 휘발성 메모리가 존재하고 , 일부는 읽기 뿐 아니라 쓰기도 가능하지만 여러이유로
| 종류 | 특징 |
|---|---|
| PROM | 한 번만 프로그래밍 가능 |
| EPROM | 자외선으로 초기화 후 재사용 가능 |
| EEPROM | 전기적 방식으로 재기록 가능 |
| Flash | EEPROM 기반, 빠르고 널리 사용됨 (USB, SSD 등) |
이런 ROM 들이 있습니다요.
메인 메모리에는 보통 DRAM 이 사용된다고 했는데요.
데이터는 버스라고 하는 전기회로를 통해서 프로세서(CPU)와 DRAM 간에 앞뒤로 교환돼요.
그래서 매 번 데이터의 전송은 버스 트랜잭션이라고 부르는 단계로 이뤄져요.
제어라인 들은 이 트랜잭션들이 어디로 가는지 어떤상태인지에 대한 정보를 관리해요.

버스가 많네요. 교대 환승센터인가? 하하하
하드디스크 같은 커다란 저장장치를 말해요.
디스크는 RAM 이 수천MB 를 저장할 수 있는거에 비해서
수백에서 수천기가바이트 단위까지 저장할수 있는 대표적인 저장장치예요.
근데 SRAM 보다 백만 배 더 오래걸리고, DRAM 보다 십만배 더 오래 걸림.

디스크들은 원판 ( Flatter ) 로 구성돼요.
원판의 중심에 있는 Spindle Motor 은 원판을 고정된 회전율로 회전시켜주고
보통 분당 5,400 번에서 15,000 번 정도 회전해요.
디스크의 용량은 다음 요소들에 의해서 결정돼요.
디스크 생산업체는 이 면적밀도를 높이기 위해서 쉼없이 노력하고 있고
매 몇 년 마다 2배로 늘어나고 있어요.
디스크는 위의 그림처럼 Actuator Arm 을 사용해서 데이터를 읽거나 써요.
드라이브는 팔의 반지름 방향의 축을 따라 헤드를 어떤 표면 위에도 위치시킬수 있는데 이걸 seek라고 합니다.
이 헤드는 약 0.1 마이크론 높이로 디스크 표면 위의 얇은 공기 쿠션을 시속 80km로 말 그대로 날아다니는데
디스크 위의 작은 먼지는 비유하면 거대한 바위와 같아요.
그래서 일반적으로 디스크들은 언제나 공기 밀폐 용기에 넣어서 밀봉해요.
디스크는 데이터를 섹터 크기의 블록으로 읽고 기록한다.
운영체제는 디스크 섹터를 메인메모리로 읽어들이는 것 같은 입출력 연산을 수행하려 할 때,
디스크 컨트롤러로 명령을 보내서 특정 논리블록 번호를 읽어들이게 해요.
그래픽카드, 모니터, 마우스, 키보드, 디스크 같은 입출력 장치들은 입출력 버스로 CPU와 메인메모리에 연결돼요.
이 장에서는 SSD 가 위의 회전형 디스크에 비해 어떻게 다르고, 어떤 장점과 단점이 있는지, 성능과 수명에 어떤 영향을 미치는지 설명해요.
일단 SSD 는 HDD 보다 빠르지만 , 가격은 같은 메모리크기 기준 30배 비쌉니다.

SSD 는 HDD를 대체하는 매력적인 제품이예요.
기계적 부품이 없고 디스크대신 플래시 메모리 칩을 사용해요.
이 플래시 메모리 칩과 논리블록들에 대한 요청을 하부 물리 디바이스에 대한 접근으로 번역하는 플래시 번역 계층으로 이뤄져 있어요.

플래시 메모리는 저렇게
블록과 그 하부에 페이지들로 이뤄져 있어요.
데이터를 읽을 때는 페이지 단위로 읽고 써요.
한 개의 페이지는 자신이 속한 블록이 지워진 직후에 쓰일 수 있는데 이건 블록의 모든 비트가 1로 설정되는 것을 의미해요.
하나의 블록은 대략 10만번 정도 쓰이면 노후화되서 못써요.

이 표를 보면 랜덤 쓰기 작업이 랜덤 읽기 작업보다 느린데
등의 이유로 랜덤 쓰기가 랜덤 읽기보다 빠를 가능성은 매우 낮아요.
저장 기술의 저장용량은 늘었지만 속도는 크게 향상되지 않았어요.
DRAM 과 디스크 가격은 엄청나게 저렴해졌지만, 속도는 거의 그대로예요.
반대로 CPU 는 속도만 향상돼서 둘 사이의 병목이 더 커졌어요.
그나마 SRAM 이 유일하게 cpu 속도 향상과 비슷하게 따라가는 저장기술이예요.
그래서
SRAM <-> DRAM <-> SSD <-> 디스크
사이의 성능 차이를 계층적 구조로 설계해서
자주 쓰는 데이터를 상위 계층 (SRAM)에 배치하는 것이 핵심입니다.
잘 작성한 컴퓨터 프로그램은 좋은 지역성을 보여줘요.
지역성이란? 프로그램이 메모리를 접근하는 패턴의 규칙성을 말해요.
지역성이 좋은 프로그램은 최근 접근한 데이터나 그 근처의 데이터를 자주 다시 접근해요.
지역성에는 두가지 유형이 있어요. 하나는 시간적 지역성 (Temporal Locality)고 하나는 공간적 지역성 (spatial Locality) 예요.
for루프 내에서 반복적으로 같은 변수를 사용할 때를 말해요.arr[i] arr[i+1]... 을 의미해요.지역성을 이해하고 있으면 이후에 나올 캐시구조 이해가 쉬워져요.
현대 시스템은 CPU 는 빠르지만 DRAM/디스크는 상대적으로 느리기 때문에 지역성이 없다면 병목현상이 심각해져요
캐시, 가상메모리, 디스크 버퍼 등 결국 모두 최근 접근 데이터만 빠르게 접근하려는 구조입니다.
간단한 C언어 코드를 살펴보면서 좋은 지역성과 나쁜 지역성을 보겠습니다.
Stride 는 보폭 이란 뜻인데요.
메모리를 0x00 에서 0x01로 가면 Stride-1 이라고 합니다.
int sumarrayrows(int a[M][N]) {
for (i = 0; i < M; i++)
for (j = 0; j < N; j++)
sum += a[i][j];
}
c 는 row-major order로 배열을 저장해요.
배열을 0,0 0,1 0,2 순서대로 접근하고 있기 때문에 메모리상 연속됩니다.
Stried-1 이예요.
따라서 좋은 공간적 지역성을 가지고 있다고 볼 수 있어요.
int sumarraycols(int a[M][N]) {
for (j = 0; j < N; j++)
for (i = 0; i < M; i++)
sum += a[i][j];
}
같은 코드인데 j와 i의 순서만 바뀌었어요.
배열을 0,0 1,0 2,0 순서로 접근하게 됩니다.
Stried-k 입니다. k는 배열의 크기에 따라 달라질거예요.
캐시 효율이 나쁘기 때문에 나쁜 공간적 지역성을 가지게 됐어요.
인스트럭션(명령어)도 데이터처럼 지역성에 영향을 받는다는 개념입니다.
앞서 설명한 개념들을 정리했습니다.
이번 3장에서는 컴퓨터 메모리 시스템으 구조와 그것이 하드웨어와 소프트웨어의 지역성 특성과 어떻게 잘 맞물려 작동하는지를 설명해요.
저장장치 기술들은 매우 다양한 접근시간을 가지고 있고 , 빠를수록 비싸고 용량은 작아요.
느릴수록 싸고 용량은 커요.
그럼에도 불구하고 지역성이 좋고 계층적 메모리 구조를 잘짜면 빠르고 효율적인 실행을 할 수 있어요.
| 레벨 | 구성 요소 | 속도 | 비용 (per byte) | 용량 |
|---|---|---|---|---|
| L0 | CPU 레지스터 | 매우 빠름 (1 cycle) | 매우 비쌈 | 매우 작음 |
| L1 | L1 캐시 (SRAM) | 빠름 | 비쌈 | 작음 |
| L2 | L2 캐시 (SRAM) | 중간 | 조금 비쌈 | 보통 |
| L3 | L3 캐시 (SRAM) | 느림 | 보통 | 큼 |
| L4 | 메인 메모리 (DRAM) | 훨씬 느림 | 저렴 | 훨씬 큼 |
| L5 | 로컬 디스크 (HDD/SSD) | 매우 느림 | 매우 저렴 | 수백 GB~TB |
| L6 | 원격 저장소 (NFS, Web) | 가장 느림 | 네트워크 비용 포함 | 무제한 |
상위 계층은 하위 계층의 데이터를 캐시하는 역할이예요.
빠른 접근이 가능하도록요.
캐시 ( cache )는 더 느리고 큰 저장장치의 일부 데이터를 빠르고 작은 저장장치에 블록 단위로 복사해서 속도 향상을 꾀하는 구조예요.
이 과정을 캐싱이라고 합니다.
일반적으로 메모리 단위에서의 캐싱은 자동으로 동작하며, 프로그래머가 직접 신경 쓸 필요는 없어요.
캐시는 기본적으로 항상 블록 단위로 이동해요.
각 블록은 고유한 주소를 가지고 고정크기 혹은 가변크기일 수 있습니다.

어떤 프로그램이 레벨 K+1 로부터 데이터 data 를 필요로 할 때,
우선 K에 저장된 블록들 중 하나에서 data 를 찾아요.
어라? 있네 캐시히트~~ 라고 합니다.
반면, 만약 요청한 데이터가 캐시에 없을 경우에는 하위 계층에서 블록을 가져와요.
그리고 캐시에 있는 기존 블록을 교체합니다.
어떤 블록을 교체할지는 교체정책에 따라 정해지는데요.
| 미스 종류 | 설명 |
|---|---|
| Cold miss (Compulsory miss) | 캐시가 비어 있어서 처음 불러올 때 발생 |
| Conflict miss | 서로 다른 데이터가 같은 캐시 슬롯에 매핑되어 충돌 발생 (예: (i mod N) 방식) |
| Capacity miss | 작업에 필요한 데이터 양이 캐시 크기를 초과하여 계속 쫓겨남 |
레벨 K 의 캐시가 아예 비어있다면 , cold cache라고 불려요.
서로 다른데이터가 같은 블록을 계속 캐시하려고 하면 충돌이 나서 계속 블록을 교체해줘야 해요.
이걸 충돌미스 Conflict miss 라고 부릅니다.
무한루프 같은거 걸리면 Capacity miss나요.
| 계층 | 캐시 종류 | 관리 주체 |
|---|---|---|
| L0 (레지스터) | 4/8-byte 단어 | 컴파일러 |
| L1~L3 캐시 | 64-byte 블록 | 하드웨어 |
| 메인 메모리 | 4KB 페이지 | OS + 하드웨어 |
| 디스크, 웹 캐시 등 | 섹터/파일 조각 | OS, 디스크 컨트롤러, 웹 브라우저 등 |
다양한 저장장치의 캐시 관리주체는 다음과 같은데
이는 앞서 언급한 바와 같이 자동으로 동작하기 때문에, 프로그래머가 신경 쓸 필요는 없습니다.
원래 과거 시스템의 메모리 계층구조는 단순했어요
레지스터 -> 메인메모리 -> 디스크
근데 CPU랑 메인메모리간의 성능 차이가 커짐에 따라서 SRAM기반의 L1 캐시가 CPU와 메인메모리사이에 추가 됐어요.
| 캐시 계층 | 위치 | 접근 속도 | 특징 |
|---|---|---|---|
| L1 캐시 | CPU와 레지스터 사이 | 약 4 사이클 | 작고 빠름 (CPU 칩 안에 있음) |
| L2 캐시 | L1과 메인 메모리 사이 | 약 10 사이클 | L1보다 크고 약간 느림 |
| L3 캐시 | L2와 메인 메모리 사이 | 약 50 사이클 | 더 크지만 더 느림 |
| 메인 메모리 | 가장 느림 (수백 사이클 이상) | - | DRAM 기반, 큰 용량 |
요즘 컴퓨터는 좀 더 많은 캐시메모리를 포함하고 있답니다?
캐시는 Set 으로 나뉘고 각 Set은 E개의 Line을 가져요.
각 Line 은 이렇게 구성돼요.

set 하나하나마다 valid 와 Tag를 가지고 있고
0, 1, 2......B-1 에는 블록 단위로 데이터가 저장돼 있어요.
메모리 주소가 A가 있어요. 우린 지금까지 그냥 0x00 으로만 써왔지만 메모리 주소는 원래 다음의 세 부분으로 나뉘어요.
| 구성 요소 | 의미 | 크기 (비트 수 32비트면 m = 32) |
|---|---|---|
| Tag (t) | 어떤 블록인지 구분 | t = m − (s + b) |
| Set Index (s) | 어떤 Set에 저장할건지 | s = log₂(S) |
| Block Offset (b) | 블록 내에서 몇 번째 바이트인지 | b = log₂(B) |
즉, 주소 A로부터
없으면 블록교체 해야겠지?
직접 매핑 캐시는 Set 하나에 단 하나의 line ( E = 1 )만 저장하는 가장 단순한 캐시구조예요.
이거 왜 하냐면 이게 젤 허접이라 이해하기 쉬워서래요.
우리가 어떤 데이터를 가져올 때, 캐시 데이터가 있으면 캐시 적중! 없으면 캐시 미스! 잖아요.
이렇게 한 데이터를 뽑아내기 위해 수행하는 작업은 다음의 세 단계로 이루어져요.
(1) 집합 선택, (2) 라인 매칭, (3)데이터 추출
우선 요청주소 0xff1010이라고 할게요 그냥.
임의로 여기서 Set Index 를 추출해서 어느 Set을 사용할지 결정해요.
예: S = 2이면 2비트 -> 4개의 set (0~3번)
선택한 set의 line이 유효(valid)하며 , Tag가 일치하면 hit.
아니면 miss -> 블록을 메모리에서 가져와 해당 line 에 저장.
캐시 적중이 발생했을 경우예요.
요청주소 0xff1010에서 Block Offset을 사용해 block 내에서 원하는 데이터의 위치를 찾아요.
어느 라인을 교체해야 할지 고민할 필요가 없다.
직접매핑은 정확히 한 개의 라인을 가지고 있으므로
현재 라인을 새롭게 선입한 라인으로 교체한다.
충돌미스는 실제 프로그램에서 일반적이고 어려운 성능문제를 야기해요.
직접매핑에서는 일반적으로 크기가 2의 제곱인 배열에 접근할 때 발생해요.
예를 들어서
1 float dotprod(float x[8], float y[8])
2 {
3 float sum = 0.0;
4 int i;
5
6 for (i = 0; i < 8; i++)
7 sum += x[i] * y[i];
8 return sum;
9 }
이 코드를 보면 2의 제곱인 8만큼의 크기를 가진 float 배열이 있어요.
float 이 4바이트고 x주소가 0부터 시작해서 32바이트의 주소값을 가질거예요.
float 이 4바이트고 y주소는 32부터 이어서 곧바로 시작한다고 가정할게요.
블록크기는 보통 BYTE 단위인데 ( 디스크 블록이랑 다른거임 ) 16B 라고 가정할게요.
하나의 블록은 4개의 float을 저장할 수 있네요.
| 요소 | 주소 | Set index | 포함된 block (16B) | 포함된 요소들 |
|---|---|---|---|---|
| x[0] | 0 | 0 | 0~15 | x[0] ~ x[3] |
| y[0] | 32 | 0 | 32~47 | y[0] ~ y[3] |
| x[4] | 16 | 1 | 16~31 | x[4] ~ x[7] |
| y[4] | 48 | 1 | 48~63 | y[4] ~ y[7] |
Set Index 계산법으로는 x[0]과 y[0] 이 계속 같은 set index 를 참조해서
계속 교체해줘야 해요.
그래서 이런 문제를 방지하기 위해 이렇게 코드를 짜기도 한대요 ㅋㅋ
float x[8];
float pad[4]; // 16 bytes 패딩
float y[8];
이거 진짜예요?
직접매핑 캐시에서 충돌문제는 딱 하나의 라인만 가진다는 제한에서 오는 거예요.
각 집합이 하나 이상의 캐시 라인을 가지는걸 집합결합성 캐시라고 해요.
이건 직접매핑 캐시와 동일하게 주소 0xff1010에서 Set Index를 추출해서 사용해요.
한 Set안에 여러개의 Line이 있으므로, 요청된 tag를 모든 Line 에 대해서 검사해요.
빈 line 이 있으면 그곳에 넣으면 돼요.
모든 line이 꽉차면? => 어떤 line 을 내보낼지 결정해야 해요.
라인교체정책:
| 정책 | 설명 |
|---|---|
| Random | 랜덤으로 하나 골라서 교체 |
| LRU (Least Recently Used) | 가장 오래 전에 사용된 line을 교체 |
| LFU (Least Frequently Used) | 과거에 사용 빈도가 가장 낮은 line 교체 |
완전결합성 캐시는
모든 캐시라인들을 갖는 하나의 집합으로 구성돼요.

주소 0xff1010에서 Set Index를 찾을 필요가 없습니다.
그야 Set이 하나뿐인걸요?
집합결합성과 비슷하지만 모든 line 을 비교해요.
병렬로 tag 비교하구 , valid bit 확인합니다.
일치하면 block offset으로 확인!
지금까지 저 위에서 봤던건 전부 데이터 읽기와 관련된 글이였어요.
읽기는 단순해요. 캐시가 있으면 반환해주고 없으면 하위계층에서 찾아서 저장해요.
쓰기는 어떻게 처리할지 여러 전략이 존재해요.
| 전략 | 설명 | 장점 | 단점 |
|---|---|---|---|
| Write-through | 즉시 하위 계층에 씀 | 단순, 일관성 보장 | 모든 쓰기에 버스 트래픽 발생 → 느림 |
| Write-back | 캐시에서만 먼저 수정하고, 나중에 교체 시 하위 계층에 씀 | 버스 트래픽 줄임 (지역성 이용) | 구현 복잡, dirty bit 필요 |
| 전략 | 설명 | 장점 | 단점 |
|---|---|---|---|
| Write-allocate | 블록을 캐시에 불러온 후 수정 | 지역성 활용 (향후 재사용 기대) | miss 시 블록 전체 복사 필요 |
| No-write-allocate | 캐시에 올리지 않고 하위 계층에 바로 씀 | 구현 단순 | 지역성 활용 X |
기억 꿀팁
“Write-back은 캐시에 맡기고, Write-through는 메모리에 맡긴다.”
“Write-allocate는 불러오고 수정, No-write-allocate는 그냥 바로 써라.”
지금까지는 캐시가 오직 프로그램 데이터만을 보관한다고 가정해 왔는데요.
사실 캐시들은 데이터 뿐 아니라 명령어들도 저장할 수 있어요.
인스트럭션 "만" 보관하는 캐시는 i-cahce라고 불러요.
데이터 "만" 보관하는 캐시는 d-cache라고 불러요.
둘다 저장하는건 Unified cache라고 부릅니다.
현대 대부분의 CPU는 대부분 L1에서 i-cahce와 d-cache를 구분해서 사용해요.
왜냐면
뭔소린지모름 걍 읽은 거 적음
| 레벨 | 유형 | 소유 | 용도 |
|---|---|---|---|
| L1 i-cache | 명령어 전용 | Core마다 개별 | 빠른 instruction fetch |
| L1 d-cache | 데이터 전용 | Core마다 개별 | 빠른 데이터 access |
| L2 cache | Unified | Core마다 개별 | i-cache + d-cache 통합 |
| L3 cache | Unified | 모든 코어가 공유 | 크고 느림, 캐시 간 공유 데이터 역할 |
L3에서 공유하는 이유는 멀티코어 환경에서 데이터 공유할라고

캐시 성능은 보통 다음과 같은 지표로 확인해요.
| 용어 | 설명 |
|---|---|
| Miss rate | 전체 메모리 참조 중 캐시에 없는 비율 = #misses / #references |
| Hit rate | 히트 비율 = 1 - miss rate |
| Hit time | 캐시에서 데이터 가져오는 시간 (L1은 수 클럭 사이클 수준) |
| Miss penalty | 캐시 미스 후 하위 계층에서 데이터 가져오는 데 드는 추가 시간 |
캐시를 크게 만들면 적중비율을 당연히 높여주겠죠.
근데 크기가 큰 메모리는 또 빠르게 동작하게 만들기 어려워요.
그 결과 크기가 큰 캐시는 적중하더라도 가져오는 시간이 길어져요.
그래서 캐시성능 지표중
hit rate 는 올라도 hit time 도 커져요. ( 속도가느려짐)
크기가 큰 블록들은 공간 지역성을 활용해서 적중 비율을 높여줄 수 있어요.
근데 같은 캐시 크기에서 라인 수가 감소해요. 왜냐하면 사이즈는 같은데 블록이 커지면 라인갯수가 줄어들 수밖에 없으니까요.
이건 시간지역성이 더 많은 프로그램에서는 적중 비율에 타격을 줄 수 있어요.
그리고 miss 가 났을 때 블록크기가 크니까 복사시간도 오래걸리게 돼요.
이건 집합당 라인수가 미치는 영향이예요.
라인수가 크면 미스가 줄어서 바꾸는 위험성을 감소시켜요.
근데 구현이 어렵고, 블록 크기의 영향과 마찬가지로 miss 되었을 때 교체비용이 많이들어요.
write-through 바로쓰기 전략은 간단하고 캐시와 메모리가 항상 같은 데이터를 갖고있어요.
구현도 쉽고 데이터를 메모리에서 바로 가져오기 때문에 write miss 에 특별히 복잡한 처리가 필요하지 않아요.
근데 모든 쓰기 동작이 바로 메모리까지 전달되기 때문에, 버스 사용량이 많아지고, 메모리 대역폭을 많이 차지해요.
쓰기 동작이 빈번한 경우는 메모리 접근이 많아져서 CPU가 기다리는 시간이 늘어날 수 있어요.
write-back 은 캐시안에만 저장하고 메모리에는 저장하지 않는 전략이예요.
메모리 접근 횟수가 감소하고 메모리 대역폭이 절약돼요.
단점은 복잡도가 증가하고 캐시와 메모리 사이에 데이터 차이가 존재해서, 멀티프로세서에서는 복잡한 동기화가 필요해요.
만약 캐시 블록이 여러번 변경됐어도 메모리에 반영이 늦어서 , 에러 발생시 데이터가 사라질 위험도 있어요.