- 메모리 계층
제한된 환경에서 가급적 높은 성능을 낼 수 있도록 프로그래밍 하기 위해서는 메모리의 특성을 잘파악하고 있어야 한다.
컴퓨터를 구성하는 요소 중에서 임시적이든,영구적이든 저장 기능을 조금이라도 가지고 있으면 무조건 메모리의 범위에 포함이 된다.
[메인 메모리]
가장 먼저 떠올릴 수 있는 것은 메인 메모리인 램이다. 보다 정확히 말하면 D램 계열의 메모리이다. 참고로 메인 메모릭 반드시 램이어야 할 이유는 없다. 따라서 메인 메모리와 램에는 등호 관계가 성립하지 않는다. 그러나 거의 모든 컴퓨터가 메인메모리로 램을 사용한다.
[레지스터]
레지스터 또한 당연히 메모리이다. CPU안에 내장되어 있어서 연산을 위한 저장소를 제공한다.
[캐쉬]
캐쉬는 D램보다 빠른 S램으로 구성하는데,램이라는 단어는 메인 메모리를 의미하는 용도로 사용되므로 ,캐쉬 메모리는 그냥 캐쉬라고 표현한다. 캐쉬는 CPU와 램 사이에서 중간 저장소 역할을 하는 메모리이다. 그리고 요즘은 캐쉬가 CPU에 내장되어 있다고 표현하기도 하는데, "캐쉬 메모리는 원래 CPU의 일부로 존재하는 메모리 개념이 아니다. CPU에 근접해 있느 메모리 개념이다."
CPU의 일부로 존재하는 메모리는 레지스터이다.
다시 정리하면, 캐쉬는 CPU에 가장 근접해 있는 메모리이다.
[하드디스크와 이외의 저장 장치들]
하드디스크도 당연히 메모리이다. 하드디스크는 크고 작은 파일들을 저장하기 위한 용도로도 사용되지만, 프로그램 실행에 있어서도 중요한 의미를 지닌다.
그밖에 SD카드,CD_ROM과 같은 I/O 장치들도 메모리에 해당한다.
"프로그래머는 레지스터,캐쉬, 메인 메모리,하드디스크뿐만 아니라 그 밖의 I/O 장치들과의 입/출력 타이밍 및 대기 시간 등을 가장 중요한 요소로 생각하고 항상 고민해야 한다.
프로그램이 실행되는 동안에 메모리가 하는 역할은 데이터의 입력 및 출력이다. 따라서 기본적인 역할은 모든 메모리가 동일하다. 그 대상이 레지스터이건,하드디스크이건 모두가 동일하다. 그렇다면 어떠한 부분에 있어서 차이를 보이는가
가장 큰 차이점은 CPU를 기준으로 얼마나 멀리 떨어져 있느냐이다. 레지스터는 CPU에 가깝다 못해 CPU 안에 존재하는 메모리이다. 그 다음으로 가까이 있는 것은 캐쉬 메모리이고 그다음이 메인 메모리이다. 가장 멀리에 있는 것이 하드디스크이다.
CPU와 가까이에 있을수록 빠르고 멀리 있을수록 속도가 느리다. 어렵게 생각할 것이없다 가까우니깐 빠른것이다 (물론 물리적 특성도 있다). CPU의 레지스터 접근은 별다른 절차가 필요없다. 그러나 메인 메모리에 접근하기 위해서는 몇몇 복잡한 과정을 거쳐야 한다. 대표적인 것이 버스 인터페이스 컨트롤이다. 데이터를 입출력하기 위해서는 메모리 버스를 거쳐야 하기 때문에 그만큼 더 느리다.
그렇다면 레지스터의 용량을 크게해서 레지스터를 통해 모든일을 해결하면 안될까? 다만 문제는 기술이고 비용이다. CPU 근처로 대용량의 메모리를 가져 갈수록 기술적인 문제들도 발생하고 비용도 훨씬 많이 든다. 하드디스크의 용량을 수깁 기가바이트 늘리는 것보다 캐쉬메모리 1MB를 늘리는 데 드는 비용이 훨씬 크다.
가장 위에 위치한 레지스터는 차지하고 있는 크기가 가장 작지만 가장 빠르다
그다음은 L1,L2캐쉬이다 둘다 캐쉬이지만 L1캐쉬가 CPU에 보다 근접해 있다.
그 다음은 메인메모리이다. 캐쉬보다는 크지만 상대적으로 느리다
마지막 하드디스크는 가장 크지만 가장 느리다.
하드디스크에 있는 내용은 프로그램의 실행을 위해 메인메모리와 같이 위계층으로 레지스터 까지 이동한다. 즉 모든 메모리의 역할이 피라미드 구조에서 자신보다 아래에 있는 메모리를 캐쉬(자주 사용되는 메모리의 일부를 저장해서 속도를 향상시키는 것)하기 위해서 존재하는 것으로 이해하면된다.
역순으로 다시 한번 살펴보자 연산에 필요한 데이터가 레지스터에 존재하지 않는다면 L1캐쉬를 다시 l2...마지막으로 하드디스크에서 읽어 들이게 된다. 그렇다면 하드디스크에서 위 연산을 통해 레지스터로 최종 읽어오게 된다.
만약 이러한 식으로 피라미드 구조의 아래 단(하드디스크)까지 접근 하는 빈도수가 총 메모리 접근 빈도수의 반만 차지해도 상당히 느려 질것이다. 차리리 캐쉬가 없는편이 더 빠를수있을거같기도하다.
하지만 우리가 사용하는 시스템에서는 캐쉬 메모리가 높은 성능 향상을 가져온다. 그만큼 연산에 필요한 데이터가 캐쉬 메모리에 존재할 확률이 아주 높다는 뜻이다. "메인 메모리를 제외한 L1 캐쉬와 L2캐쉬에, 연산에 필요한 데이터가 존재할 확률이 90프로 이상 된다!" 그렇다면 메인메모리까지 포함한다면 하드디스크에서 데이터를 읽어오는 빈도수는 몇퍼센트밖에 되지 않을 것이다.
02 . 캐쉬와 캐쉬 알고리즘
컴퓨터 프로그램을 유심히 관찰해 보면,공통적으로 지니는 일반적인 특성이 하나 존재한다. 이러한 공통적인 소프트웨어 특성을 연구하고 분석한 다음, 하드웨어 구조적으로 캐쉬 메모리라는 것을 등장시켜서 성능 향상을 가져오게 된 것이다.
대부분의 함수가 특정 연산을 하기 위해서 지역 변수가 종종 선언되고 이러한 지역변수의 특성은 선언 및 초기화 이후 다양한 값으로 변경도 되고,값을 얻기 위한 참조도 빈번하게 일어난다는 것이다. 이러한 특성을 가리켜 템퍼럴 로컬리티라한다. 템퍼럴 로컬리리티란 프로그램 실행 시 한번 접근이 이뤄진 주소의 메모리 영역은 자주 접근하게 된다는 프로그램 특성을 표현할 때 사용하는 말이다.
스페이셜 로컬리티 : 프로그램 실행 시 접근하는 메모리 영역은 이미 접근이 이뤄진 영역의 근처일 확률이 높다는 프로그램 성격
캐쉬 프렌들리 코드 : 캐쉬의 성능을 고려한 프로그램
소프트웨어의 스페이셜 로컬리티와 템퍼럴 로컬리티의 특성을 캐쉬가 어떻게 반영하는가?
위그림에서 ALU 연산과정 중에서 필요한 데이터가 있다면 이를 레지스터로 이동시켜야한다. 만약 필요한 데이터가 0x1000번지에 존재하는 데이터라면 이주소에 해당하는 데이터를 레지스터로 가져오기 위해서 L1 캐쉬를 찾아본다.
만약 L1캐쉬에 해당 데이터가 존재할 경우 이를 가리켜 캐쉬 힛이 발생했다고 하며 이 ㅔ이터를 레지스터로 이동시킨다. 반대로 없을경우 캐쉬미스가 발새앟면 L2를 탐색한다.
여기서 데이터의 이동은 블록단위로 지정한다 L2 캐쉬에서 L1 캐쉬로 이동하는 데이터의 단위는 0x1000번지의 데이터를 포함하는 하나의 블록이 된다. 단순히 필요로 하는 데이터만 보내는 것이 아니라, 블록 단위로 전송을 해서 공간적 지역서의 특성을 성능 향상에 활용한다.
메모리의 피라미드 구조상 애라로 내려갈수록 블록 크기는 커진다. 이는 아래에 존재하는 메모리 일수록 접근 횟수를 줄이는 효과를 가져다 준다. 아래에 존재하는 메모리일수록 속도가 느리기 때문에 접근의 횟수를 줄이는 것이 성능 향상에 많은 도움이 된다.
캐쉬 미스가 발생했을때 고려하게 되는 부분 - 운영체제가 동작을 하고, 프로그램이 실행되는 동안에는 하드디스크를 제외한 모든 메모리가 항상 채워져 있다. 따라서 L1 캐쉬에서 캐쉬 미스가 발생해서 L2 캐쉬로 부터 데이터 블록을 읽어 들일 때 다음과 같은 고민이 발생한다. "어디다 저장하지?"
꽉 차 있는 L1 캐쉬에 데이터를 저장하려면 당연히 기존에 저장한 데이터를 밀어내야만 하는데 이때에도 아무런 규칙 없이 밀어내는 것이 아니라, 잘 정의된 블록 교체 알고리즘에 의해서 데이터를 밀어내게 된다. 블록 교체 알고리즘은 캐쉬 교체 정책에 따라서 달라질 수있는데 보편적으로 거론되는것이 LRU알고리즘이다 단어의 의미대로 가장 오래 전에 참조된 블록을 밀어내는 알고리즘이다.
03 . 가상 메모리
-물리 주소
"데이터라는 것은 컴파일이 완료된 운영체제와 그 운영체제를 바탕으로 동작하는 프로그램을 총칭하는 것이다."
운영체제 코드와 프로그램을 하나의 바이너리 코드로 생서해서 시스템에 로딩함
위 그림에서 램 용량이 16M 바이트라면 접근가능한 메모리 영역은 0번지부터 (16 1024 1024) -1 번지. 이것은 실제 물리적인 메인 메모리의 주소 범위에 해당하며, 이렇게 주소를 할당하는 것을 가리켜 물리적 주소 지정이라 한다. 물리적 주소 지정의 특징은 메인 메모리 크기에 따라서 지정 가능한 주소의 범위가 결정되는 것이다.
이처럼 물리적 주소 지정을 하게 되면 CPU 입장에서는 접근 가능한 주소의 범위가 제한됨. 이것은 프로그래머가 할당할 수 있는 주소 범위가 제한적이라는 뜻도 된다.
지금까지 설명한 물리적 주소 지정은 우리가 쉽게 이해할 수 있는 메모리 구조이다. 메인 메모리크기가 16M바이트이면 이 범위 안에서 운영체제와 프로그램을 로딩하고 프로그램 실행과정에서 메모리를 할당해야만 한다. 이는 당연한 이치 이다.
32 비트 시스템에서 프로세스 생성 시 4g바이트의 메모리를 할당받을수 있다. 그러나 메인 메모리의 크기는 여기에 턱없이 부족하다. 따라서 4G바이트는 실제 존재하지 않는 가상의 주소라는 결론부터 내릴 수 있다. 이렇게 가상의 주소를 지정하는 것을 가리켜 가상 메모리 공간이라 한다.
하드디스크의 여유공간은 수십 기가 바이트나되고 메인 메모리의역할을 하드디스크가 못하지는 않는다 (다만 좀 느릴뿐) 단 두가지를 고려해주어야 한다.
[첫 번째 문제 : 선 할당으로 인한 부담]
프로세스를 생성할 때마다 4G바이트씩 하드시크에 할당해주는것은 아비도 심하고 시간도 오래걸린다
[두 번째 문제 : 느린 속도의 개선 필요성]
하드디스크는 너무 느리다 속도를 개선해줘야한다.
가상 메모리 시스템을 이해하면 위 두 문제의 답을 해결할수있다.
가상메모리 시스템을 구현하는 방법은 표준으로 정해져 있지 않다. 그러나 대부분의 시스템에서 페이징이라는 기법을 사용한다.
가상 메모리 시스템을 설명하기위한 약간의 설정
가정 1. 16비트 시스템, 따라서 0부터 64K -1까지 주소 지정가능
가정 2. 프로세스별로 64K바이트 메모리 할당, 물론 가상 메모리 할당
가정 3. 메인 메모리 16K 바이트, 즉 램 용량이 16K 바이트
16비트 시스템에 메인 메모리를 16K바이트로 잡은 이유는 설명의 편의성을 위한것이다.
실제 메모리는 16K바이트가 전부인데 프로세스를 생성할 때마다 64K바이트를 할당하고자 하니 문제다. 특히 16K번지 이상의메모리는 접근조차 불가능하다 최대 할당 가능한 메모리 번지는 16K-1번지이다.
MMU는 16K바이트밖에 존재하지 않는 메모리를 64K 바이트가 존재하는것처럼 CPU가 느끼도록 컨트롤하는 역할을 한다.
CPU가 1K번지를 시작으로 20바이트를 할당해달라는 요청을 하면 MMU는 메인 메모리에서 아직 사용되지 않은 메모리 블록하나를 골라서 할당한다. 근데왜 20바이트만 요청했는데 메인메모리 4K바이트 블록을 모두 지정하는가.
위그림에서는 메모리 할당의 단위가 4K바이트인셈이다
블록의 단위를 16바이트 단위 정도로 최소화하거나 단위를 없앨 수도 있지만 그만큼MMU는 복잡한 일을 해야하고 이는 속도의 감소로 이어지게 된다.
나머지 공간은 불필요한 할당이 아니라 공간적 지역성을 생각하면 곧 사용 될것이다.
이러한 블록을 하드웨어 입장에서는 페이지 프레임이라 하고 소프트웨어 입장에서는 페이지라 한다. 즉 페이지 플레임은 실제 메인 메모리 블록을 의미하고,페이지는 가상 메모리 블록을 의미한다. 물론 페이지 프레임과 페이지의 크기는 일치한다.
위 그림은 가상 메모리와 그에 해당하는 실제 물리 메모리가 어떻게 매핑되는지 보여준다. 현재 가상 메모리 0K-4K는 물리 메모리의 가장 위쪽에 매핑되어 있다(MMU에 의해 매핑된다) 따라서 CPU가 0K~4K 사이에 존재하는 데이터를 요구할 경우 MMU는 매핑된 물리 메모리를 참조해서 데이터를 전송해준다.
위와 같은 구조를 유지하면서 CPU에게 서비스를 제공하기 위해서 MMU는 내부적으로 아래와 같은 정보를 유지하고 있다 . 여기서 주목할 부분은 CPU 요청한 주소의 데이터를 MMU가 해석해 나가는 과정이다. 페이지 크기를 4K 바이트 기반으로 하여 설명하고 있다.
위 그림에서 페이지 테이블을 보자. 페이지의 크기를 4K 바이트로 정의하는 경우 64K바이트의 메모리 공간에서 얻을 수 있는 총 페이지 개수는 16개이다. 페이지 0은 0K-4k의 메모리주소를 나타낸다.
페이지 테이블의 키는 페이지 숫자이다. 그리고 값은 해당 페이지가 존재하는 페이지 프레임의 시작번지이다. 이러한 테이블 구성을 지닐 경우 페이지 테이블을 참조해서 가상 주소를 실제 할당되어 있는 물리주소로 변환할 수있다.
[가상 주소 시스템 2]
만약 메모리가 부족할경우 어떻게 할것인가? 앞서 언급했듯이 하드디스크도 램과 비교해서 속도를 제외하면 그 기능에 있어서 조금도 부족함이 없는 메모리이다. 이러한 하드디스크를 메인 메모리로 확장해서 문제를 해결할 수 있다. 조금 구체적으로 말하자면 스왑파일 이라는 개념을 도입해서 램에 해당하는 메인 메모리를 하드디스크로 확장한 것이다.
수십 기가바이트 이상의 여유 공간을 메인 메모리로 확장하여 쓸 수 있다는 사실은 아주 큰 장점이다. 이제 메모리 할당 과정에서 부족한 부분은 하드디스크의 여유공간으로 대체한다.
하드 디스크는 스왑 파일을 통해서 메인 메모리를 보조해 주는 것이지 램과 동일한 성격의 메인 메모리 역할을 하는것이 아니다.
프로세스 A가 실행을 멈추고 프로세스 B를 실행시키고자할때 A의데이터를 스왑파일에 저장하고 B 와교체한다.
가상 메모리 구성에 필요한 시스템의 기본 기능(특히 메모리할당 및 변환)은 MMU에 의해 제공되고, 이를 바탕으로 Windows 운영체제의 VMM이 사용자 측면 기능을 완성한다.
이것만은 알고 갑시다.
컴퓨터 시스템의 메모리는 피라미드 계층 구조를갖는다. 피라미드의 제일 아래에는 하드디스크가 존재하고, 가장 위에는 레지스터가 존재한다. 피라미드 구조의 아래쪽으로 내려갈수록 CPU와 거리가 멀어져 속도는 느리되 용량은 엄청난 크기로 증가한다.
캐쉬 메모리가 성능에 도움을 주는 이유는 프로그램의 일반적인 특성 두 가지 때문인다. 대부분의 프로그램은 실행과정에서 한번 접근이 이뤄진 주소의 메모리 영역에 자주 접근하는 특성이 있다. 이러한 특성을 가리켜 템퍼럴 로컬리티라 한다. 또한 이미 접근이 이뤄진 영역의 근처에 접근할 확률이 높다는 특성도 지닌다. 이를 가리켜 스페이셜 로컬리티라 한다. 이 두 가지 특성 때문에 캐쉬는 성능 향상에 많은 도움이 된다.
메인 메모리의 부족함을 가상 메모리라는 메커니즘으로 극복하고 있다. 이는 하드디스크를 메인 메모리로 확장하여 완성시킨 개념이다. 가상 메모리로 인해 Windows상에서 생성되는 모든 프로세스는 메인 메로리의 크기에 상관없이 약속된 메모리 공간을 할당 받을 수 있다. 가상 메모리의 동작 원리를 정확히 이해하자.
가상 메모리의 개념을 이해하면, 전체 메모리의 구조가 피라미드 관계상에서 캐쉬 관계를 형성하고 있음을 알 수 있다. 캐쉬 메모리가 레지스터와 메인 메모리 사이에서 캐쉬 역할을 하고 있는 것처럼,메인 메모리도 캐쉬 메모리와 하드 디스크 사이에서 캐쉬 역할을 하고 있다. 이러한 메모리 계층별 관계를 이해하고 있어야 한다.
가상 메모리와 실제 물리 메모리 사이에서 주소의 변환을 담당하는 것은 소프트웨어가 아니라 MMU라는 하드웨어 블록이다(소프트웨어로 할 수 없는 일은 아니다). 이는 아주 빈번히 일어나는 연산이기 때문에 하드웨어로 구성해야 성능에 영향을 미치지 않는다. 즉 가상 메모리의 구현은 MMU라는 하드웨어 블록의 도움을 받는다.