메모리 계층
메모리는 계층구조를 가지는 형태로 발전해왔으며 CPU 프로세서와 가까울 수록 데이터 접근시간이 빠르고 비용이 비싸다.
메인 메모리 DRAM으로 구현되고 캐시는 SRAM으로 구현되며 USB와 같은 휴대 메모리는 플래시 메모리를 사용한다. 하드 디스크와 같은 가장 느린 저장장치는 자기 테이프로 구현되어있다.
메모리의 지역성
어떤 데이터를 접근하기 위해 기본적인 원리가 있는데, 최근에 참조한 데이터가 다시 참조할 가능성이 높다는 시간적 지역성과 최근에 참조한 데이터와 근처에 있는 주소의 데이터를 참조할 가능성이 높을 것이라는 공간적 지역성이 사용된다.
메모리 계층에서 데이터의 이동
메모리 계층구조는 여러 층으로 구성되어 있지만 데이터는 인접한 두 계층 사이에서만 한 번에 복사가 된다.
이런 두 계층간에 복사되는 정보의 최소 단위를 블록 혹은 라인이라고 한다.
프로세서가 요구한 데이터 정보가 상위 계층의 어떤 블록에 있을 때 이를 적중(hit)이라고 한다. 만약 데이터 정보가 해당 블록에 없다면, 실패(miss)라 하고 이에 따라 하위 계층에서 다시 블록을 찾게된다.
적중 시간(hit time)는 상위 계층을 접근하는 시간을 의미한다. 실패 손실(miss penalty)는 하위 계층에서 해당 블록을 가져와서 상위 계층 블록과 교체하는 시간에다 블록을 프로세서에 보내는데 걸리는 시간을 더한 값이다.
캐시의 기본
캐시는 메인 메모리와 프로세서 사이의 메모리를 지칭한다.
각 워드 데이터가 캐시 내 딱 한 장소에만 있을 수 있다면 해당 데이터가 있는지 없는지 바로 알 수 있다. 이를 직접 사상(Direct Mapping)이라고 한다. 해당 워드가 찾아야하는 위치에 없을 때 캐시 미스가 발생한다.
직접 사상 방식은 한 셋"set"에 하나의 워드만 보관하고 워드 주소를 보고 유일한 캐시 블록을 찾아야한다. 이는 워드 주소를 전체 캐시 블록 갯수만큼 modulo 연산을 해주면 구할 수 있다.
캐시 인덱스 = (블록 주소) modulo (캐시 내 존재하는 전체 캐시 블록수)
전체 블록 개수가 $2^{10}$개가 있고 찾아야하는 주소가 16bit이라면 $2^{16} modulo 2^{10}$, 즉 하위 10비트를 블록 주소로 사용하게 된다. 해당 블록이 실제 주소인지 알기 위해 나머지 상위 6비트를 "tag"필드로 따로 저장하여 사용한다.
또한 처음 캐시를 사용할 경우 첫 주소는 해당 캐시에 있을 수 없다. 이 캐시 블록의 유효함을 나타내기위해 유효 비트(1)를 설정한다.
- 블록은 워드(4바이트)배수로 정렬되기 때문에 모든 주소의 맨 오른쪽 2비트는 워드 내부의 바이트를 나타낸다. 최하위 2비트는 캐시 블록의 주소를 찾을 때 사용하지 않는다.
다음 가정에서 주소와 캐시를 인덱싱하는 방법을 살펴본다.
- 64-bit 주소
- 직접 사상 캐시
- $2^n$ 블록 갯수를 가지는 캐시
- $2^m$ 워드 크기(2^{m+2}비트)를 가지는 블록 사이즈 (데이터를 저장한다.)
태그 필드의 비트 크기는 다음과 같다.
64 - (n + m + 2)
직접 사상 캐시의 전체 비트 수는 다음과 같다.
$2^n \times (블록 크기 + 태그 크기 + 유효 비트)$
일반적으로 캐시 크기는 유효 비트와 태그 필드의 크기를 제외하고 데이터 크기만 따진다. 그림의 캐시는 1워드 블록이 1024개 있으므로 4KiB 캐시라고 할 수 있다.
ex) 16KiB 데이터와 4워드 블록을 직접 사상캐시의 구현에 필요한 전체 비트는? (64 비트 주소에서 계산한다.)
블록 갯수 = 16KiB = 4096 워드 데이터 / 4워드 블록 = 1024 블록 갯수를 가진다.
따라서 64 비트 주소에서 10비트는 캐시를 인덱스하는데 사용하고 상위 52 비트는 태그 필드로 사용된다.
캐시의 크기는 $2^{10} \times (4 \times 32 + 52 + 1) = 2^{10} \times 179 = 179 Kibibits = 22.4 KiB$가 된다.
실제 데이터의 크기에 1.4배 정도 더 크다.
블록 크기와 실패율
캐시 블록 크기가 크다면 데이터를 충분히 담을 수 있으므로 실패율이 낮아진다. 하지만 블록 크기가 너무 크다면 거꾸로 블록 갯수가 작아져서 동일한 주소를 가지는 다른 데이터에서 블록 간 충돌이 증가한다. 따라서 어느 일정 크기를 넘어서면 실패율이 증가하게 된다. 또한 블록을 주 메모리에서 가져오는 지연시간이 커지기 때문에 성능은 더 악화된다.
캐시 실패 처리
캐시 실패의 종류는 다음과 같다.
1) Cold-Start miss
첫번째 접근 시도는 항상 실패하게 된다.
2) Capacity miss
기존 캐시로 인해 용량이 부족하여 블록을 교체하는 상황이다.
3) Conflict miss
동일한 캐시 인덱스이지만 다른 태그를 갖는 데이터 접근으로 블록 교체가 이루어진다.
4) Invalidation miss
여러 프로세서에 의해 공유되는 블록에 의해 발생한다.
캐시 실패 처리는 프로세서의 제어 유닛과 별도의 제어기가 같이 수행하게 된다. 제어기는 메모리를 접근하고 캐시를 채우게된다. 캐시 실패의 처리는 모든 레지스터의 상태를 인터럽트와 달리 파이프라인 지연을 발생시킨다.
- 과정
원래의 PC값을 메모리로 보낸다.
메인 메모리에 읽기 동작을 지시하고 접근을 끝낼 때까지 기다린다.
메모리에서 인출한 데이터를 데이터 부분에 쓰고 태그 필드에 상위 비트를 쓰고 유효비트를 1로 설정한다.
명령어 수행을 첫 단계부터 다시 시작하여 캐시에서 명령어를 가져온다.
쓰기 처리
쓰기 처리는 읽기와 다르게 작동한다. 저장 명령의 경우 데이터를 데이터 캐시에만 쓰고 메인 메모리에 쓰지 않는 경우 메인 메모리는 캐시와 다른 값을 가지게 된다. 이 경우 캐시와 메모리는 불일치하게 된다.
가장 쉬운 방법은 데이터를 메모리와 캐시에 같이 쓰는 즉시 쓰기 (Write-Throuh)이다. 즉시 쓰기는 메인 메모리 접근을 직접적으로 요구하므로 성능이 매우 좋지 않다. 대신 캐시에만 썻다가 캐시에서 해당 블록이 내보내질 때만 쓸 수 있도록 하는 나중 쓰기(Write-Back)이 더 많이 사용된다. 나중 쓰기 방식은 구현하기가 더 까다롭다.
참고 : Computer Organization and Design RISC-V edition
'Computer Science 기본 지식 > 컴퓨터 구조' 카테고리의 다른 글
[컴퓨터 구조] 스택 포인터와 프레임 포인터 (0) | 2021.05.01 |
---|---|
[컴퓨터 구조] 11. 캐시 성능 측정 및 향상 (0) | 2021.04.26 |
[컴퓨터 구조] 9. 명령어 수준 병렬성 (0) | 2021.04.19 |
[컴퓨터 구조] 8. 파이프 라인 해저드 (0) | 2021.04.19 |
[컴퓨터 구조] 7. 파이프 라인된 데이터 패스 (0) | 2021.04.18 |