Post

캐시


컴퓨터 구조

컴퓨터 구조를 공부하면서 알게된 내용을 요약해서 작성해보자.

메모리와 CPU의 상호 작용

  • 폰 노이만 구조
    • 기계 명령어와 명령어에서 사용하는 데이터가 메모리에 저장되어 있어야 한다.
    • CPU가 기계 명령어를 실행할 때 먼저 명령어를 메모리에서 읽어야 한다.
    • 명령어를 실행하는 과정에서 데이터를 메모리에서 읽어야 할 수도 있다.
    • 명령어가 계산 결과 저장을 포함할 때 이를 다시 메모리에 저장해야 한다.
  • 프로그램이 실행되는 동안 CPU는 메모리와 빈번하게 상호 작용 해야한다.

CPU와 메모리의 속도 차이

  • 시스템 성능은 상대적으로 느린 쪽에 맞추어 제한된다.
  • CPU와 메모리는 시간이 지날수록 속도 차이가 벌어지고 있다.
  • CPU는 명령어를 실행할 때 메모리를 기다리고 있을 수 밖에 없다.
  • 일반적인 시스템에서 메모리의 속도는 CPU의 100분의 1이다.

캐시

  • CPU는 메모리 사이에 캐시 계층이 추가되어 있다.
  • 캐시는 가격이 비싸고 용량이 제한적이지만 접근 속도가 CPU 속도에 필적한다.
  • 캐시 안에는 최근에 메모리에서 얻은 데이터가 저장된다.
  • CPU는 메모리에서 명령어와 데이터를 꺼내야 할 때도 무조건 먼저 캐시에서 해당 내용을 찾는다.
  • 캐시에 내용이 있으면 메모리에 접근할 필요가 없어 빠르게 실행 가능하다.
  • 이 경우 CPU는 직접 메모리를 읽고 쓰지 않기 때문에 CPU와 메모리 사이의 속도 차이를 보완할 수 있다.
  • x86 같은 최신 CPU와 메모리 사이에는 L1, L2, L3 캐시로 구분된다.
    • L1 캐시: 레지스터 접근 속도에 비해 약간 느리다. 대략 4클럭 주기가 소요된다.
    • L2 캐시: 대략 10클럭 주기가 소요된다.
    • L3 캐시: 대략 50클럭 주기가 소요된다.
  • 캐시 단계에 따라 접근 속도는 낮아지지만 용량은 증가한다.
  • L1, L2, L3 캐시, CPU 코어는 레지스터 칩 내에 묶여 패키징 되어 있다.
  • CPU -> L1 -> L2 -> L3 -> 메모리 접근 순이다.

캐시 갱신

  • 캐시를 추가하면 성능이 향상될 수 있지만 대가도 따른다.
  • 불일치 문제가 생긴다.
    • 캐시의 데이터는 갱신되었지만 메모리의 데이터는 갱신되지 않는 상황
  • 불일치 문제를 해결하는 가장 간단한 방법은 캐시 갱신할 때 메모리도 함께 갱신한다.
    • 연속 기입 방식이라고 한다.
    • CPU는 메모리가 갱신될 때 까지 대기해야 한다.
    • 연속 기입 방식은 동기식 설계 방법에 해당한다.
  • 캐시 용량에도 한계가 있어 용량이 부족하면 반드시 자주 사용되지 않는 데이터를 제거해야 한다. 이 때 캐시에서 제거된 데이터가 수정된 적이 있다면 이를 메모리에 갱신해야 한다. 이렇게 캐시의 갱신과 메모리의 갱신이 분리되므로 이 방식은 비동기에 해당하며 이를 후기입 방식이라고 한다.
  • 후기입 방식은 연속 기입보다 훨씬 복잡하지만 성능은 분명히 더 낫다.

다중 코어 캐시의 일관성

  • CPU가 코어 여러 개를 가지고 있다면 또 다른 문제가 발생한다.
  • 코어가 2개일 경우 복사본 두 개를 가지고 있다. 코어1이 캐시를 갱신할 때, 코어2 캐시는 동기적으로 수정되지 않는다.
  • 최신 CPU에는 고전적인 MESI 규칙같은 다중 코어 캐시의 일관성을 유지하는 규칙이 있다.

메모리를 디스크의 캐시로 활용하기

  • 디스크는 탐색을 위해 10ms 가량의 시간이 소요된다.
  • 메모리와 비교하면 메모리 접근 속도는 디스크 탐색 속도보다 10만 배 가량 빠르다.
  • 컴퓨터 시스템의 메모리 사용률은 일반적으로 100%에 도달하지 않으며, 항상 일부 공간이 남아 있다. 운영 체제는 이 여유 메모리 공간을 디스크의 캐시로 활용하여 디스크에서 데이터를 읽어 오는 일을 최소화한다. 이것은 리눅스 운영 체제에서 페이지 캐시의 기본 원리에 해당한다.
  • 시스템이 충돌하거나 정전 같은 사태가 발생하면 데이터가 유실될 수 있어 대부분의 입출력 라이브러리가 동기화(sync)또는 캐시 비우기(flush) 함수를 제공한다.
  • 메모리를 디스크의 캐시로 취급하여 파일 접근 속도를 크게 향상시키고 디스크 입출력을 줄일 수 있게 되었다. 하지만 파일을 읽고 쓸 때 메모리가 디스크의 캐시 역할만 하는 것은 아니다.

가상 메모리와 디스크

  • 파일을 읽고 쓸 때 메모리를 디스크의 캐시로 쓸 수 있는데 이때 디스크는 메모리의 창고 역할을 할 수 있다.
  • 일부 프로세스에서 자주 사용하지 않은 메모리 데이터를 디스크에 기록하고 이 데이터가 차지하던 물리 메모리 공간을 해제한다. 그러면 N + 1번째 프로세스가 다시 메모리를 요청할 수 있다.
  • 디스크가 메모리의 일부 작업을 넘겨받기에 모든 프로세스가 요청하는 메모리 크기는 물리 메모리를 넘어설 수 있으며, 더 이상 물리 메모리에 국한되지 않는다.
  • 이 모든 과정을 프로그래머가 인식할 수 없고, 운영 체제가 대신 한다.
  • 프로세스 주소 공간의 데이터는 디스크로 대체될 수 있으므로 프로그램이 디스크 입출력을 포함하고 있지 않더라도, 메모리 사용률이 매우 높을 경우에는 CPU가 프로그램을 실행할 때도 디스크에 접근해야 할 수 있다.

CPU의 메모리 읽기

  • CPU가 볼 수 있는 것은 모두 가상 메모리 주소다.
  • 읽기, 쓰기 명령어가 사용하는 것도 가상 메모리 주소이며, 이 주소는 실제 물리 메모리 주소로 변환되어야 한다. 변환이 완료되면 캐시를 찾고, L1, L2, L3 캐시 중 어떤 계층이든 찾을 수 있다면 바로 직접 반환하며, 찾을 수 없을 때만 메모리에 접근한다.
  • 가상 메모리의 존재로 프로세스의 데이터는 디스크에 임시로 보관되어 있을 수 있다.

분산 저장 지원

  • 대용량 데이터의 저장 문제를 해결하는 것은 분산 파일 시스템이다.
    • 장치 한 대로 부족하다면 여러 대를 사용한다.
  • 사용자 장치는 분산 파일 시스템을 직접 장착할 수 있고, 로컬 디스크는 원격의 분산 파일 시스템에서 전송된 파일을 저장한다. 네트워크를 통하지 않고 로컬 디스크에 직접 접근하므로 로컬 디스크를 원격의 분산 파일 시스템의 캐시로 간주할 수 있다.
  • 각 계층의 저장 용량은 반드시 다음 계층보다 작아야 한다.
    • 그렇지 않으면 캐시를 직접 메모리로 사용하면 되기 때문이다.
  • 전체 저장 체계가 최대 성능을 발휘하려면 프로그램이 매우 캐시 친화적이어야 한다.
  • 아래 계층으로 내려갈수록 낮은 비용과 속도를 가진 장치를 사용하여 데이터를 저장하고, 적은 대가만으로 CPU가 거의 최대 속도로 프로그램을 실행할 수 있도록 하는데 캐시의 설계 사상은 필수 불가결한 존재다.