캐시 메모리

2021. 11. 8. 13:15CS 지식


캐시 메모리


캐시 메모리는 RAM의 느린 속도를 극복하기 위해서 등장한 메모리로, CPU 내부에 존재한다.

 

일반적으로 데이터를 로드할 때 캐시 메모리에 저장하는데, 이 때 그 데이터 뿐만 아니라 인접한 데이터도 같이 로드하게 된다. (64 바이트의 크기를 가지는 캐시 라인 단위로)

이는 어떤 데이터에 접근할 때 인접한 데이터 또한 접근할 확률이 높기 때문이다.

데이터 로드 시점에, 캐시 메모리에 이미 해당 데이터가 저장돼있어서 캐시에서 데이터를 로드한다면 이를 캐시 히트라고 하고, 아까와 같이 캐시 메모리에 저장되어있지 않아 메모리에서 로드하는 상황을 캐시 미스라고 한다.

 

캐시 히트 확률이 높다면, 로드하는 시간이 그만큼 줄어들기에 성능이 좋아진다.

그럼 캐시 히트를 자주 발생시키려면 어떻게 해야할까?

 

★캐시에 저장할 데이터가 시간적, 공간적으로 지역성(locality)를 가져야 한다.

시간적 지역성은 한번 사용한 변수를 여러번 사용할 확률이 높다는 것,

공간적 지역성은 사용한 변수 근처의 변수를 사용할 확률이 높다는 것.

그래서 데이터 보존의 이유가 없다면 지역 변수로 변수를 선언하여 공간적, 시간적으로 같은 캐시라인에 더 많이 접근하도록 하면 성능상 좋다.

★추가로, 공간적으로 인접하다면 밑에 언급할 캐시 메모리 매핑의 원리 때문에, 캐시 스와핑을 방지할 수 있다.(인접하다면 tag가 같을 확률이 높을테니.) 이런 측면에서, 리스트보다 배열이 성능상 유리하다.

(이렇게 로컬리티를 가진다면, 메모리의 관점에서 메모리 단편화를 방지하기 때문에 페이지 아웃 확률 또한 줄여준다.)

 

또한, 의도적으로 alignas 키워드를 사용하여 캐시라인을 분리하여 성능을 높일 수 있다.

변수 선언시 alignas 키워드를 통해 변수들의 캐시라인을 정할 수 있는데,

1. 관련성있는 변수들끼리 같은 캐시라인으로 묶거나

2. 변경의 빈도가 낮은 변수들과, 변경의 빈도가 높은 변수들의 캐시라인을 분리하는 것이 성능에 좋다.

- 멀티프로세서 환경에서 캐시라인 내부 정보를 변경할 경우에, 다른 프로세서의 해당 캐시라인이 무효화되기 때문

 

캐시 교체 정책

LRU(Least Recently Used) 알고리즘 : 가장 오래전에 참조한 블록을 캐시에서 밀어낸다.

 

캐시 메모리 구성

캐시 메모리는 L1, L2, L3 캐시 메모리로 나뉜다. (L은 level의 약자이다.)

level이 낮을수록 속도가 빠르고 용량이 적다.

작업 관리자를 통해 이들의 용량을 확인할 수 있다.

 

위 상황에서 코어는 2개고, L1 캐시는 128KB, L2 캐시는 512KB, L3 캐시는 4.0MB 이다.

L1 캐시와 L2 캐시는 코어마다 가지고 있어서 용량을 각각 나누어서 쓰고, L3는 공동으로 사용한다.

  CORE1 CORE2
L1 64KB 64KB
L2 256KB 256KB
L3 4.0MB

 

이 중 L1 캐시는 Instruction CacheData Cache로 나뉜다.

메모리 쓰기 정책 write through는 변경시마다 실제 메모리에 반영하는 것이고,

write back은 캐시 메모리에만 반영하는 것이다.

  Instruction Cache Data Cache
용량 32KB 32KB
캐시 메모리 쓰기 정책 write through write back

 

Data Cache의 쓰기 정책이 write back이기 때문에,

멀티쓰레드 환경에서 코어 별로 같은 메모리 변수의 값을 다르게 가지고 있는 문제가 발생할 수 있다.

그래서 멀티쓰레드 환경에서 여러개의 코어들이 같은 캐시 라인을 저장하고 있을 때,

한 코어에서 해당 캐시 라인에 적재된 변수를 변경하면 다른 코어들의 해당 캐시라인은 무효화된다.

이후 다른 코어들에서 접근시 이 코어의 캐시라인을 로드한다. (이것도 역시 캐시 미스이다.)

 


캐시 메모리 매핑


실제 물리 메모리에서 캐시 메모리로 데이터를 불러올 때, 어떤 방식으로 캐시 메모리에 저장을 할까?

 

크게 세 가지의 방법이 있다.

직접 매핑(Direct Mapping)
연관 매핑(Associate Mapping)
n-way 연관 매핑(N-Way Associate Mapping)

 

1. 직접 매핑

캐시 메모리에 저장하는 단위는 캐시라인, 또는 블록이라고도 한다. 64바이트의 크기를 가지기 때문에,

데이터 캐시의 경우 32KB이기에 32768B / 64B = 512개의 블록을 저장할 수 있다.

 

512개의 캐시라인(블록) 수는 9개의 비트로 표현할 수 있다.

그리고 하나의 블록에는 64 바이트의 데이터를 바이트 단위로 6개의 비트로 표현할 수 있다.

 

그래서 직접 매핑에서는 실제 물리 주소를 이용하여

블록을 구분하는 것을 index (line number)로, 블록 내부의 바이트 단위 구분을 offset으로, 남는 비트를 tag로 나눈다.

32비트 주소의 경우 표와 같이 구분되는 것이다.

tag index offset
17bit 9bit 6bit

  

이러한 캐시 구조로 저장하면 인덱스로 바로 접근이 가능하기 때문에 성능이 좋지만, 하나의 인덱스에는 태그가 다른 두개의 값이 들어올 수 없기 때문에 스와핑이 발생하고, 캐시 미스가 자주 발생할 수 있다.

 

ex) 0x01010101010101010000000000111111, 0x11111111111111111000000000111111

의 경우 인덱스값이 같아서 같은 캐시 메모리 공간에 저장되어야하지만 태그값이 다르기에 스와핑이 발생함.

 

2. 연관 매핑

연관 매핑은 이러한 직접 매핑의 캐시 미스를 극복하고자 등장했다.

연관 매핑은 index를 없애고 오직 tag로만 블록들을 구분한다. 캐시 라인이 들어가야할 캐시 메모리의 위치는 정해져있지 않고, 아무 블록 공간에나 들어가면 된다.

tag offset
26bit 6bit

 

이러한 캐시 구조로 저장하면 캐시 메모리에 접근할 때 모든 블록의 태그를 비교해야하기 때문에 성능이 나쁘지만, 어느 공간에나 캐시 메모리가 저장될 수 있으므로 캐시 미스율이 낮다.

 

3. 집적(n-way) 연관 매핑 (set associative mapping)

위 두 매핑 방법은 서로 다른 장점과 단점을 갖고 있다. 이 두 가지 방법을 섞은 것이 바로 n-way 연관 매핑이다. 실제 대부분의 PC에서 사용되는 방식이다.

CPU-Z로 PC를 확인해보면 캐시 부분에 8-way라고 쓰여져있다.

n-way 연관매핑은 직접 매핑에서 바로 이 way라는 개념이 추가된 것이다.

직접 매핑의 방식처럼 인덱스로 접근하지만, 접근하는 것이 블록 자체가 아니라 블록들이 n개 모인 set에 접근하도록 하고,

그 내부에서는 연관매핑과 같이 tag를 통해 탐색하는 것이다.

 

tag index offset
20bit 6bit 6bit

 

이 방식은 직접매핑과 비교했을 때,

좁은 영역에서만 메모리를 사용할 경우에는 직접매핑이 효과적이지만,

이곳저곳 메모리 사용이 많은 현재에는 집적연관매핑 방법이 효과적이다.

 

 

'CS 지식' 카테고리의 다른 글

프로세스, 스레드, 동기화  (1) 2023.05.10
메모리  (0) 2021.11.08
호출 규약  (0) 2021.10.25
endianness (엔디안)  (0) 2021.10.23
코드, 데이터, 힙, 스택 영역  (0) 2021.10.23