지금까지 이전 글에서 설계했던 Single Cycle MIPS는 상대적으로 디자인하기는 쉽지만, 성능을 원하는만큼 끌어올리는데 한계가 있다.
구체적으로는 Critical Path가 만들어내는 딜레이보다 주기를 짧게 가져갈 수는 없다.
그렇다면 어떻게 해야 CPU의 성능을 더 끌어올릴 수 있을까?
제일 직관적인 방법 중 하나는 마이크로 아키텍쳐는 그대로 두고, 아키텍처를 구성하는 트랜지스터의 공정을 개선해서 성능을 끌어올리는 방법이 있다.
하지만 이 방법도 결국에는 한계가 있다.
트랜지스터가 작아지면 작아질수록 전자의 지름에 가까워지는데, 반도체의 크기를 전자의 지름보다 작게할 순 없기 때문이다. (반도체도 결국 전류의 흐름 == 전자의 이동에 의한 소자이기 때문이다.)
따라서 한번 공정이 아닌 아키텍처 개선을 통해 성능을 개선해보자.
이때 사용할 수 있는 개선 방법으로 '파이프라인' 방법이 있다.
Pipeline
파이프라인은 컴퓨터의 성능 측정과 관련된 내용과 함께 정리했었다.
간단하게 복습하자면, 빨래를 할 때 세탁 - 건조 - 빨래 개기 단계를 다 마치고나서 다음 단계를 수행하면 하나의 일을 끝내는데는 90분의 시간이 걸리고, 4개 일을 끝내는 데에는 360분의 시간이 걸리므로, 1시간에 0.67개의 일을 수행한 것과 같았다.
하지만 파이프라인을 적용하면 하나의 일을 끝내는 시간 (response time)은 90분으로 그대로 유지되지만, 4개의 일을 끝내는 시간을 210분으로 줄여서 시간당 1.14개의 일을 처리하도록 전체 throughput을 끌어올릴 수 있었다.
이제 파이프라이닝을 명령어 관점에서도 적용해보자.
파이프라인에서 각각의 단계를 '스테이지' 라고 하는데, 명령어를 실행하는 과정을 아래와 같이 5개의 단계로 나누었다고 해보자.
명령어를 가져오고, 레지스터 파일에 접근해서 레지스터 값을 읽고, ALU 연산을 수행한 뒤, 메모리에 접근해서 데이터를 읽거나 쓴다. 그리고 최종 결과를 레지스터에 쓰는 행위까지 총 5가지 단계로 나누었다.
그리고 각 단계는 위 표와 같은 시간이 소모된다고 하자.
이때 만약 lw 명령어를 처리한다고 생각했을 때, 파이프라인을 적용하지 않으면 위와 같이 하나의 명령어를 처리하는데는 8ns 시간이 걸린다.
3개의 ㅁ여령어를 처리하는데에는 24ns 시간이 필요하다.
만약 파이프라인을 적용한다면 위와 같이 표가 그려진다.
이 때 하나의 명령어를 실행하는 시간은 9ns 로 약간 늘어났으나, 그 다음 명령어부터는 처리하는데 추가로 2ns 시간밖에 필요하지 않기 때문에 3개 명령어를 실행하는데, 13ns 의 시간밖에 필요하지 않다.
(파이프라인은 response time의 시간이 오히려 늘어날 수도 있다. 다만 설명할 때, response time이 줄어들지 않는다는 방향으로 강조할 뿐이다. response time의 시간이 늘어나더라도 전체 throughput 에서는 크게 성능이 개선된다.)
지금처럼 스테이지를 5개를 나눈 경우에는 최대 5개의 명령어를 중첩해서 실행할 수 있다.
(거꾸로 최대 5개의 명령어를 중첩할 수 있다고 하면 5개의 스테이지를 사용하는 것과 같다.)
또한 성능 측면에서 파이프라인 기법을 사용할 때는 각 스테이지에서 소모되는 시간의 균형을 맞추는 것이 중요하다.
어떤 건 100ns인데, 어떤건 1ns 이면, 나중에 중첩했을 때 100ns 단계와 1ns 단계가 중첩될 수 있고, 그러면 의미없이 99ns의 시간을 손해보게 되어 스루풋 개선이 크지 않을 것이다.
만약 밸런스가 잘 맞춰져있다면 이상적으로 생각했을 때 파이프라인에서 하나의 명령어를 실행하는데 걸리는 시간은 기존 방식에서 명령어를 실행하는 시간을 state수로 나눈 값이 될 것이다.
Pipeline MIPS
MIPS의 ISA는 파이프라인을 고려해두고 설계되었다.
모든 명령어의 크기를 32-bit로 통일시켜서 fetch 단계를 간소화 시키고,
명령어의 포맷을 규격화해서 1번의 decode단계를 통해 명령어를 실행할 수 있다.
load/store 주소처리 방식도 주소값을 계산하고 메모리에 접근하는 2단계로 나눴기에 주소값을 계산하는 데 ALU 사용과 산술연산을 하는데 ALU 사용을 통일화 시켜 똑같이 3번째 단계에 ALU를 사용할 수 있도록 하였다.
메모리 내에서 모든 피연산자들이 4의 배수로 정렬되어있는 것도 파이프라인을 쉽게 만드는데 도움을 준다.
명령어를 읽을 때, 데이터를 읽을 때 무조건 4의 배수로 읽어오면 되기 때문에 명령어, 데이터를 읽어오는 과정을 1사이클에 수행할 수 있다. 만약 길이가 제각각이라면 어떤 경우에는 두 사이클에 걸쳐서 명령어/메모리를 처리해야 할 수도 있을 것이다.
우선 MIPS 에 파이프라인을 적용하기 위해 기존 Single Cycle 회로를 길게 늘리고 각 단계에 세로줄을 그어 구분해보았다.
첫번째 stage는 IF (Instruction Fetch) 단계이다.
데이터패스인 PC 값을 4 증가시키고 Instruction Memory에서 명령어를 가져오는 단계이다.
두번째 stage 는 ID (Instruction Deocde) 단계이다.
명령어를 해석하고, 필요한 extend를 진행한다.
레지스터파일은 ID와 WB에서 모두 쓰이지만 일단 쪼갤 수 없으니 ID 단계에 넣어둔다.
세번재 stage 는 EX (Execute) 단계이다.
ALU에 의해 연산이 일어난다.
4번째 stage 는 MEM (Memory) 단계이다.
메모리에 접근해 데이터를 쓰거나 읽는 단계이다.
5번째 stage 는 WB (Write Back) 단계이다.
메모리에서 값을 읽었거나, ALU의 연산 결과를 다시 레지스터에 쓰는 과정이다.
이때 그림에서 검은색 데이터패스는 자신의 단계 안에서만 실행되거나, 다음 단계로 순차적으로 연결되는 경우를 의미한다. (왼쪽에서 오른쪽)
주황색 데이터패스는 자신의 단계를 역행해서 이전 단계로 넘어가는 경우를 나타낸다.
우선 WB단계에 있는 주황색 데이터패스는 딱히 문제를 일으키지 않는다.
왜냐하면 WB단계에서 레지스터 파일에 데이터를 덮어쓸 때 지금 실행하고 있는 명령어에는 영향을 미치지 않는다.
예를 들어 add s0, s0, s1 이라고 한다면, s0 + s1 = s0 를 할 때, 이 명령어의 결과로 s0 에 값을 쓰는 시점에서는 이미 s0에서 필요한 값을 잘 읽었기 때문에 문제가 없다.
같은 이유로 위에 있는 주황색 데이터패스도 문제가 없다.
조건이 참일 때 branch할 PC값의 주소를 계산하는 데이터패스인데 현재 branch 명령어를 실행하는 시점에서는 branch 명령어를 PC에서 잘 꺼내고 잘 실행한 다음에 덮어쓰는 것이기 때문에 현재 명령어를 실행할 때는 문제가 없다.
사실 파이프라인을 실제로 진행한다는 뜻은, 이렇게 각 stage 사이에 플립플랍을 둔다는 의미와 같다.
왜냐하면 하나의 클럭에는 하나의 stage가 실행되고, 그 다음 클럭에서 이전 stage가 처리한 결과를 받아서 처리해야하기 때문에 그 이전 stage가 처리한 결과를 담아둘 임시 저장공간이 필요하기 때문이다.
이 플립플랍을 파이프라인 레지스터라고 부른다.
하지만 이렇게만 회로를 구성하면 파이프라인 방식에서는 문제가 발생할 수 있다.
그때의 문제를 '해저드' 라고 하는데, 이는 다음 글에서 정리한다.
'CS > 컴퓨터 구조' 카테고리의 다른 글
[컴퓨터 구조] 19. Pipeline MIPS (3) - Datapath & Control (0) | 2024.06.01 |
---|---|
[컴퓨터 구조] 18. Pipeline MIPS (2) - Hazard (0) | 2024.05.30 |
[컴퓨터 구조] 16. Single Cycle MIPS - 성능 (0) | 2024.05.29 |
[컴퓨터 구조] 15. Single Cycle MIPS - Control Unit (0) | 2024.05.29 |
[컴퓨터 구조] 14. Single Cycle MIPS - 회로 정리 & ALU (0) | 2024.04.21 |