토요일, 10월 15, 2016

컴퓨터의 메모리 계층 구조와 레지스터 파일


컴퓨터는 기본적으로 메모리 계층 구조를 가지는데, CPU - 캐시 - 메인 메모리 - 디스크 순서로 처리 속도가 빠르다. 실제 파일 등의 데이터는 물리적인 디스크 안에 저장이 되어 있는데, 컴퓨터를 뜯어보면 알 수 있다시피 하드 디스크는 일종의 레코드판과 같은 구조를 가지고 있다. 어떤 파일에 대해 읽기/쓰기 작업을 수행하려면 디스크 암(Arm)이 찾고자 하는 파일이 위치한 섹터가 위치한 실린더로 헤드를 움직여야 하기 때문에 탐색 시간(Seek time)이 소요되며, 디스크 자체도 회전을 하면서 필요한 섹터를 그 헤드에 위치시켜야 하므로 회전 지연(Rotation delay)이 발생한다. 게다가 디스크로부터 읽어들인 데이터를 운영체제 측에 넘겨주어야 하므로 전송 지연(Transfer delay)도 또한 발생한다.
따라서 요약을 하면 디스크에 대한 접근시간(Access time)은 다음과 같이 나타낼 수 있다.
Access time = Seek time + Rotation delay + Transfer delay
즉, 수많은 연산을 처리하기 위해서 이렇게 매번 디스크에 접근을 한다면 매우 비효율적이다. 따라서 컴퓨터는 내부적으로 더 빠른 장치로 데이터를 옮겨서 처리하려는 경향이 있다. 이러한 과정을 흔히 캐싱(Caching)이라고 한다. 물리 디스크에 있는 데이터를 메인 메모리에 올리고, 그 메인 메모리에 있는 데이터를 캐시로 불러와서 처리하는 식의 과정을 거친다. 하드웨어의 성능에 있어서 위 그림의 higher level로 갈수록 속도가 비약적으로 증가하기 때문에 소프트웨어의 관점에서도 알고리즘이나 프로그램을 짤 때 '캐시 활용률'을 중요하게 생각한다.
CPU core의 내부에는 심지어 메인 메모리와 캐시보다 더 빠른 하드웨어가 내장되어 있는데, 그것이 바로 레지스터 파일(Register file)이다. 제조사마다 다르지만 MIPS CPU의 경우, 이 Register file은 32 bit 워드 체계를 기준으로 32 bit 짜리 레지스터(Register) 32개와 PC(Program Counter)로 이루어져 있다. 각각의 Register는 32 bit 짜리 데이터를 담을 수 있는 일종의 저장소이며, 산술이나 논리 연산 등 아주 간단한 연산을 할 때 활용된다. 처리속도가 매우 빠르기 때문에 컴파일러(Compiler)가 프로그램을 컴파일할 때 이 Register들을 적극적으로 활용한다. 조사한 바에 따르면, 각 저장 장치들에 대한 access time을 CPU의 cycle 수로 측정했을 때 대략적으로 다음과 같은 수치가 나온다고 한다.
1 cycle to read a register
4 cycles to reach to L1 cache
10 cycles to reach L2 cache
75 cycles to reach L3 cache
200 cycles to reach main memory
그리고 instruction들을 수행할 때도 cycle을 매우 적게 잡아먹는다. MIPS의 CPU는 다음과 같이 거의 3~5 cycles 정도가 소모된다고 한다.
Load (5 cycles)
Store (4 cycles)
R-type (4 cycles)
Branch (3 cycles)
Jump (3 cycles)


Register file의 구조는 위의 그림과 같이 생겼다. 주소를 통과시키는 선은 5 bit, 그리고 데이터를 통과시키는 선은 32 bit를 사용하는데, 디코더(Decoder)를 떠올려보면, 5 bit로 32개의 decimal number를 가리킬 수 있기 때문이다. 즉 src1 addr, src2 addr, dst addr은 모두 Register의 number를 가리킨다. 흔히 instruction을 수행할 때 "몇 번 레지스터에 있는 값을 불러와서 연산을 하고 몇 번 레지스터에 저장해라~"라는 식으로 진행이 되기 때문에 이렇게 Register number로 indexing을 하면 매우 편리하다.
이 Register file을 Verilog 코드로 간단하게 구현하면 다음과 같다.
module regfile(input         clk,
                      input         we,
                      input  [4:0]  ra1, ra2, wa,
                      input  [31:0] wd,
                      output [31:0] rd1, rd2);
  reg [31:0] rf[31:0];
  // three ported register file
  // read two ports combinationally
  // write third port on rising edge of clock
  // register 0 hardwired to 0
  always @(posedge clk)
    if (we) rf[wa] <= wd;
  assign rd1 = (ra1 != 0) ? rf[ra1] : 0;
  assign rd2 = (ra2 != 0) ? rf[ra2] : 0;
endmodule

Reference & Source:
Computer Organization and Design by David Patterson and John Hennessy, 5th edition, Morgan Kaufmann, 2013
Lecture 4. MIPS & MIPS Instructions #1, Prof. Taeweon Suh, Computer Science & Engineering, Korea University

댓글 없음:

댓글 쓰기