파이프라인 해저드가 CPU 성능을 떨어뜨리는 방식

데이터·제어·구조적 파이프라인 해저드의 원인과 포워딩, 스톨, 분기 예측, 레지스터 리네이밍 대응 방식을 정리한다.

2026-08-14 · 최초 발행 2026-01-03

명령어가 겹쳐 흐를 때 생기는 병목

파이프라인은 여러 명령어를 단계별로 겹쳐 처리하지만, 모든 명령어가 같은 속도와 순서로 진행되지는 않는다. 앞선 명령어의 결과를 기다리거나, 분기 이후의 실행 경로가 확정되지 않거나, 동일한 하드웨어 자원을 동시에 요구하면 흐름이 막힌다. 이런 상황이 파이프라인 해저드(Pipeline Hazard)다.

해저드는 CPI를 높이고 처리량을 낮춘다. 처리할 수 없는 단계에는 버블(Bubble)이 생기며, 필요하면 파이프라인을 일시 정지하는 스톨(Stall)이 삽입된다. 원인은 크게 데이터 의존성, 분기, 자원 충돌로 나뉜다.

결과값을 기다리는 데이터 해저드

데이터 해저드는 명령어 사이의 데이터 의존성 때문에 발생한다. 이전 명령어가 만든 결과를 다음 명령어가 필요로 하는 경우가 대표적이며, RAW, WAR, WAW로 구분한다.

RAW: 쓰기 결과를 먼저 읽는 경우

RAW(Read After Write)는 쓰기 후 읽기 의존성으로, True Dependency라고도 한다. 가장 흔한 데이터 해저드다.

ADD R1, R2, R3      ; R1 = R2 + R3 (R1에 쓰기)
SUB R4, R1, R5      ; R4 = R1 - R5 (R1 읽기)

SUBR1을 읽는 시점에 ADD의 결과가 아직 레지스터에 기록되지 않았다면 잘못된 값을 읽을 수 있다.

      IF  ID  EX  MEM  WB
ADD:  1   2   3   4    5    (R1에 쓰기: 5번)
SUB:      2   3   4    5    (R1 읽기: 2번)
                             문제: 2번에 R1 읽지만 5번에 저장됨

이 경우 데이터 포워딩(Forwarding)으로 결과를 직접 전달하거나 스톨을 넣어 의존성을 해소한다.

WAR: 읽기가 끝나기 전에 쓰는 경우

WAR(Write After Read)은 읽기 후 쓰기 의존성, 즉 Anti-Dependency다. Out-of-Order 실행에서 발생한다.

SUB R4, R1, R5      ; R4 = R1 - R5 (R1 읽기)
ADD R1, R2, R3      ; R1 = R2 + R3 (R1에 쓰기)

SUBR1을 읽기 전에 ADD가 값을 덮어쓰면 안 된다. Out-of-Order 실행에서는 명령어 순서가 바뀔 수 있으므로 레지스터 리네이밍(Register Renaming)으로 대응한다.

WAW: 결과 기록 순서가 뒤집히는 경우

WAW(Write After Write)는 쓰기 후 쓰기 의존성, 즉 Output Dependency다. 이 역시 Out-of-Order 실행에서 발생한다.

ADD R1, R2, R3      ; R1 = R2 + R3 (R1에 쓰기)
SUB R1, R4, R5      ; R1 = R4 - R5 (R1에 쓰기)

최종 R1에는 SUB의 결과가 남아야 한다. 실행 순서가 달라지면 결과가 뒤바뀔 수 있으므로 레지스터 리네이밍과 순서 보장이 필요하다.

분기가 파이프라인을 비우는 순간

제어 해저드(Control Hazard)는 분기 명령어가 실행 흐름을 바꿀 때 생긴다. 분기 결과를 알기 전에도 다음 명령어를 가져와야 하므로, 잘못된 경로의 명령어가 파이프라인에 들어올 수 있다.

BEQ R1, R2, label   ; R1 == R2면 label로 분기
ADD R3, R4, R5      ; 실행될지 모름
SUB R6, R7, R8      ; 실행될지 모름
...
label:
MOV R9, R10         ; 분기 시 여기로

분기 결과는 EX 단계에서 결정되지만, IF와 ID 단계는 이미 다음 명령어를 가져와야 한다. 예측이 틀리면 잘못 가져온 명령어를 폐기하고 올바른 경로로 다시 채워야 한다.

5단계 파이프라인: 3 사이클 손실
10단계 파이프라인: 8 사이클 손실
20단계 파이프라인: 18 사이클 손실

파이프라인이 깊어질수록 이 페널티도 커진다. 대응 방법에는 정적·동적 분기 예측, 분기 지연 슬롯, 조기 분기 결정, 추측 실행이 있다.

정적 예측은 항상 Taken 또는 Not Taken으로 판단한다. 동적 예측은 분기 히스토리를 사용하며, 예측 정확도는 9095%다. 분기 지연 슬롯은 분기 명령어 뒤에 항상 실행되는 명령어를 배치하는 방식으로 MIPS, SPARC에서 사용한다. ID 단계에서 분기를 결정하면 페널티를 12 사이클 줄일 수 있다. 추측 실행은 예측 경로의 명령어를 미리 실행하고, 예측이 틀리면 결과를 폐기한다.

같은 하드웨어를 동시에 요구할 때

구조적 해저드(Structural Hazard)는 여러 명령어가 동시에 같은 하드웨어 자원을 요구할 때 발생한다. 이는 자원 배치와 수량에 관한 설계 문제다.

단일 메모리에서는 IF 단계의 명령어 읽기와 MEM 단계의 데이터 읽기·쓰기가 동시에 일어날 수 없다. 단일 ALU는 여러 명령어가 동시에 연산을 요구할 때 충돌하며, 슈퍼스칼라에서는 이런 상황이 발생할 수 있다. 레지스터 파일의 읽기·쓰기 포트가 부족해도 동시 접근이 제한된다.

명령어 캐시와 데이터 캐시를 분리하고, 여러 ALU와 다중 포트 레지스터 파일을 두면 충돌을 줄일 수 있다. 자원을 사용할 수 있을 때까지 스톨하는 방법도 가능하지만 성능은 낮아진다. 결국 하드웨어 자원의 규모는 비용과 성능 사이에서 결정된다.

포워딩, 스톨, 예측이 맡는 역할

레지스터 기록 전 결과를 넘기는 포워딩

포워딩은 EX 단계의 결과를 다음 명령어의 EX 단계로 직접 전달하는 방식이다. 메모리나 레지스터 기록 단계를 거치지 않도록 하드웨어 경로를 추가한다.

ADD R1, R2, R3      ; EX: R1 = R2 + R3
SUB R4, R1, R5      ; EX: R4 = R1 - R5

ADD의 EX 단계 결과를 SUB의 EX 단계로 전달하므로 WB 단계를 기다리지 않는다. 이 방식은 대부분의 RAW 해저드를 해결하고 스톨을 제거해 성능을 높인다. 다만 Load 명령어는 MEM 단계 뒤에 데이터가 사용 가능하므로 1 사이클 스톨이 필요하다.

기다려야 하는 의존성에는 스톨

스톨은 의존성이 해결될 때까지 파이프라인을 멈추고 NOP(No Operation)를 삽입하는 방식이다.

LOAD R1, [R2]       ; MEM: R1 = Memory[R2]
ADD R3, R1, R4      ; R1 필요
      IF  ID  EX  MEM  WB
LOAD: 1   2   3   4    5    (R1 사용 가능: 5번)
ADD:      2   3   4    5    (R1 필요: 3번)
                             1 사이클 스톨 필요
      IF  ID  EX  MEM  WB
LOAD: 1   2   3   4    5
ADD:      2   (stall) 4    5    6

스톨은 의존성을 해결할 수 있지만 CPI를 증가시킨다.

분기 예측기의 상태 변화

분기 예측은 정적 예측과 동적 예측으로 나뉜다. 정적 방식에는 Always Not Taken, Always Taken, BTFN(Backward Taken, Forward Not taken)이 있다. BTFN은 루프는 분기하고 일반적인 경우에는 분기하지 않는 방식이다.

동적 예측에는 마지막 분기 결과를 쓰는 1비트 카운터, 두 번 연속 틀려야 예측을 바꾸는 2비트 카운터, 분기 명령어별 히스토리를 갖는 지역 예측기, 전체 분기 히스토리를 쓰는 전역 예측기가 있다.

Strong Not Taken (00) ← Weak Not Taken (01)
                        ↕
Weak Taken (10) → Strong Taken (11)

예측 정확도는 정적 예측이 6070%, 1비트 카운터가 8085%, 2비트 카운터가 8590%, 고급 예측기가 9095%다.

물리 레지스터로 가짜 의존성 없애기

레지스터 리네이밍은 논리 레지스터를 물리 레지스터에 매핑해 WAR와 WAW 해저드를 제거하고 Out-of-Order 실행을 지원한다.

; 원본
SUB R1, R2, R3      ; R1 = R2 - R3
ADD R1, R4, R5      ; R1 = R4 + R5 (WAW)
MUL R6, R1, R7      ; R6 = R1 * R7

; 리네이밍 후
SUB P1, P2, P3      ; P1 = P2 - P3
ADD P2, P4, P5      ; P2 = P4 + P5 (다른 물리 레지스터)
MUL P6, P2, P7      ; P6 = P2 * P7

이 방식은 False Dependency를 제거해 병렬성을 높인다.

CPI로 드러나는 해저드 비용

이상적인 파이프라인의 CPI는 다음과 같다.

CPI = 1

해저드로 스톨이 생기면 CPI는 증가한다.

CPI = 1 + 스톨 사이클
CPI ≈ 1.2 ~ 1.5
CPI = 1 + (데이터 해저드 스톨) + (제어 해저드 스톨) + (구조적 해저드 스톨)

분기 명령어 비율이 20%, 분기 예측 실패율이 10%, 분기 페널티가 10 사이클인 경우는 다음과 같다.

평균 CPI 증가 = 0.2 × 0.1 × 10 = 0.2
총 CPI = 1 + 0.2 = 1.2
성능 = 1 / CPI = 1 / 1.2 = 83%
성능 손실 = 17%

현대 CPU가 해저드를 줄이는 방식

Out-of-Order 실행은 의존성이 없는 명령어를 먼저 실행하고 Reorder Buffer로 순서를 보장한다. 슈퍼스칼라는 여러 명령어를 동시에 실행하기 위해 여러 ALU와 메모리 포트를 제공해 구조적 해저드를 줄인다.

추측 실행은 분기 예측에 따라 명령어를 실행한 뒤 예측 실패 시 결과를 폐기한다. 고급 분기 예측은 복잡한 예측 알고리즘과 긴 히스토리를 사용하며 예측 정확도 95% 이상을 목표로 한다.

기법 해결하는 해저드 효과
데이터 포워딩 Data (RAW) 대부분의 스톨 제거
스톨 모든 해저드 성능 저하
분기 예측 Control 90~95% 페널티 제거
자원 복제 Structural 충돌 제거
레지스터 리네이밍 Data (WAR, WAW) False Dependency 제거
Out-of-Order Data 병렬성 증가

데이터 포워딩은 대부분의 RAW 해저드를 처리하고, 분기 예측은 9095%의 제어 해저드를 회피한다. 자원 복제는 구조적 충돌을 제거하며, 레지스터 리네이밍과 Out-of-Order 실행은 WAR·WAW 해저드 제거 및 병렬성 증가에 쓰인다. 실제 CPI는 이상적 파이프라인 대비 1.21.5 정도다.

파이프라인 해저드컴퓨터구조CPU분기 예측Out-of-Order 실행