파이프라이닝 (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

여기서 SUBR1 값이 필요하다.
그런데 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
    • 분기 명령어 뒤의 한 두개의 명령어를 분기 결과와 상관 없이 실행되는 명령어를 배치
    • 즉 결과에 영향을 주지 않는 명령어를 먼저 실행하고 분기가 결정되면 관련 명령어 실행