파이프라이닝 (Pipelining)
파이프라이닝은 명령어 처리 과정을 여러 단계로 나누고, 여러 명령어를 동시에 겹쳐 실행하여 CPU의 처리량을 증가시키는 기법이다.
처리 단계 예시
Fetch → Decode → Execute → Memory → Write Back
Fetch : 명령어 가져오기 Decode : 명령어 해석 Execute : 연산 수행 Memory : 메모리 접근 Write Back : 결과를 레지스터에 기록록
특징
- 각 단계가 동시에 다른 명령어를 처리
- 개별 명령어의 실행 시간은 크게 줄지 않을 수 있음
- 단위 시간당 처리 가능한 명령어 수(Throughput)가 증가
파이프라이닝 해저드 (Hazard)
파이프라이닝에서는 명령어들이 동시에 실행되면서 충돌이 발생할 수 있으며, 이를 해저드라고 한다.
1. 구조적 해저드 (Structural Hazard)
구조적 해저드는 명령어들이 동시에 실행되려고 하는데, 필요한 하드웨어 자원이 하나뿐이라서 충돌하는 상황이다.
예시 명령어 A: Memory 단계에서 데이터 메모리 접근 명령어 B: Fetch 단계에서 다음 명령어를 가져오려고 함
두 명령어가 동일한 자원을 동시에 사용하려고 하면 한쪽이 대기해야 한다.
해결 방법
-
자원 분리 (Instruction Memory와 Data Memory 분리) Instruction Memory : 명령어 Fetch 전용 Data Memory : 데이터 Load/Store 전용 즉, 명령어를 가져오는 통로와 데이터를 읽고 쓰는 통로를 분리해서 동시에 접근 가능하게 만드는 것
-
파이프라인 스톨 일부 명령어를 잠시 멈춰서 충돌을 피하는 방법
2. 데이터 해저드 (Data Hazard)
이전 명령어의 결과가 아직 생성되지 않았는데, 다음 명령어가 그 결과를 사용하려 할 때 발생
예시
ADD R1, R2, R3
SUB R4, R1, R5
여기서 SUB는 R1 값이 필요하다.
그런데 ADD의 결과가 아직 R1에 기록되기 전에 SUB가 실행되면 잘못된 값을 읽을 수 있다
종류
- RAW (Read After Write) : 쓰기 이후 읽기
- WAR (Write After Read) : 읽기 이후 쓰기
- WAW (Write After Write) : 쓰기 이후 쓰기
해결 방법
- Pipeline Stall :필요한 값이 준비될 때까지 다음 명령어를 잠시 멈추는 방식.
- 가장 단순하지만, 기다리는 동안 파이프라인에 빈 사이클이 생겨 성능이 떨어짐
- Forwarding (Bypassing) : 계산 결과를 레지스터에 완전히 기록될 때까지 기다리지 않고, 다음 명령어에게 전달하는 방식 즉 WriteBack까지 기다리지 않음
- Instruction Reordering : 명령어의 순서를 재배치
- 결과가 바뀌지 않는 범위 내에서 재배치
3. 제어 해저드 (Control Hazard)
분기 명령어로 인해 다음에 실행할 명령어 주소를 확정할 수 없는 경우 (왜냐면 다음 명령어를 실행할지, 분기할지 모르니까)
예시
CMP R1, R2
JE target
분기 결과가 확정되기 전까지 다음 명령어를 결정할 수 없음
해결 방법
- Branch Prediction(분기 예측)
- 분기 결과를 미리 예상하고 그 경로의 명령어를 먼저 실행하는 방식 (예측이 틀리면 명령어를 버려야함)
- Pipeline Flush (명령어 비우기)
- 잘못 가져오거나 실행 중이던 명령어를 비우는 방식
- 분기 예측과 함께 사용(분기가 틀리면 비우기)
- 분기 예측이 실패했을 때 잘못 가져온 명령어를 버리는 처리에 가까움
- Delayed Branch
- 분기 명령어 뒤의 한 두개의 명령어를 분기 결과와 상관 없이 실행되는 명령어를 배치
- 즉 결과에 영향을 주지 않는 명령어를 먼저 실행하고 분기가 결정되면 관련 명령어 실행