The Pipeline Hazard

Seungyun Lee·2026년 1월 22일

Computer Architecture

목록 보기
2/17

교수님이 "파이프라이닝이 뭐냐?"고 물으시면
명령어 실행 과정을 독립적인 단계로 나누어 자원을 병렬로 활용하는 기법입니다.

각 단계 사이에 파이프라인 레지스터를 두어 데이터를 임시 저장하고 흐름을 제어합니다.

목표는 Throughput을 최대화하여 CPI를 1에 가깝게 만드는 것이지만, 각종 해저드 때문에 지연(Stall)이 발생합니다.

파이프라이닝(Pipelining)

파이프라이닝은 CPU를 여러 개의 Stage(단계)로 나누고, 각 단계 사이에 Pipeline Register(파이프라인 레지스터)라는 '방화벽'을 세우는 작업입니다.

1. 파이프라인 레지스터 (The Magic of Registers)

이게 가장 중요합니다. 2교시에서 배운 5단계(IF, ID, EX, MEM, WB) 사이사이에는 레지스터가 존재합니다.

명칭: IF/ID 레지스터, ID/EX 레지스터, EX/MEM 레지스터, MEM/WB 레지스터.

역할: 각 단계가 끝날 때마다 결과물을 임시로 저장합니다.

예를 들어, 1번 명령어가 IF(인출)를 마치면 그 32비트 명령어 데이터는 IF/ID 레지스터에 저장됩니다.

다음 클락(Clock)이 오면, 1번 명령어는 ID(해석) 단계로 넘어가고, 그 빈자리에 2번 명령어가 들어와 IF를 수행합니다.

2. 파이프라인의 시간 흐름 (Space-Time Diagram)


상태: 5번 사이클을 보세요. CPU 안에는 5개의 명령어가 동시에 서로 다른 단계에서 돌아가고 있습니다.

성능: 이론적으로는 매 사이클마다 명령어 하나가 WB(완료) 됩니다. 이를 CPI(Clock cycles Per Instruction) = 1이라고 부릅니다. (이상적인 목표치)

3. 왜 파이프라이닝을 하는가? (수식적 접근)

석사 과정에서는 성능 수치를 다룹니다.Speedup=TimeunpipelinedTimepipelinedSpeedup = \frac{Time_{unpipelined}}{Time_{pipelined}}

  • 명령어 하나를 실행하는 시간(Latency)은 줄어들지 않습니다. (오히려 레지스터 지연 때문에 늘어남)
  • 하지만 처리량(Throughput), 즉 '단위 시간당 끝내는 일의 양'이 획기적으로 늘어납니다.
  • 석사 포인트: 사이클 타임(TcT_c)은 가장 느린 단계(Stage)의 시간에 맞춰집니다. 그래서 각 단계의 실행 시간을 최대한 균등하게 맞추는 것이 설계의 핵심입니다.

4. 파이프라이닝의 한계 (앞서 배운 해저드의 이유)

파이프라이닝이 완벽하려면 모든 명령어가 독립적이어야 합니다. 하지만 실제로는 그렇지 않죠.

자원 충돌: "나 지금 메모리에서 데이터 읽어야 해(MEM)!" vs "난 다음 명령어 가져와야 해(IF)!" → 구조적 해저드

데이터 의존: "나 저 앞사람이 계산 끝낼 때까지 기다려야 해!" → 데이터 해저드

흐름 변화: "잠깐, 아까 그 if문 결과 보니까 이쪽 길 아니래! 다 지워!" → 제어 해저드

Hazard

파이프라이닝은 성능을 높이지만, 명령어 간의 의존성(Dependency) 때문에 충돌이 발생합니다. 이를 해저드(Hazard)라고 부릅니다.

1. Data Hazards

뒷 명령어가 앞 명령어의 연산 결과가 필요한데, 앞 명령어가 아직 결과를 내놓지 않았을 때 발생합니다.

A. RAW (Read After Write) - 가장 핵심

상황: 1. ADD R1, R2, R3 (R1에 결과 저장 - WB 단계에서 완료) 2. SUB R4, R1, R5 (R1을 읽음 - ID 단계에서 수행)

문제: 1번의 R1 저장은 5단계(WB)에서 일어나는데, 2번의 R1 읽기는 2단계(ID)에서 일어납니다. 시간이 맞지 않습니다.

B. 해결책 1: 포워딩 (Forwarding / Bypassing)

원리: 사실 ADD의 결과값은 3단계(EX)가 끝나면 이미 ALU에서 나옵니다. 굳이 레지스터에 저장될 때까지 기다리지 말고, ALU 출력단에서 전선을 따서 다음 명령어의 ALU 입력단으로 '직송'하는 것입니다.

하드웨어 구현: ALU 입력 앞에 Mux(멀티플렉서)를 하나 더 둡니다. 평소엔 레지스터 값을 받다가, 데이터 위험이 감지되면 '포워딩된 값'을 선택합니다.

석사 포인트: 포워딩을 하려면 Forwarding Unit이 필요합니다. 이 유닛은 EX/MEM 레지스터의 목적지 번호와 현재 ID/EX 레지스터의 소스 번호를 비교해서 일치하면 '포워딩 신호'를 쏩니다.

C. 해결책 2: 스톨링 (Stalling / Bubble)

상황: 포워딩으로도 안 되는 경우 (Load-Use Hazard)

LW R1, 0(R2) (메모리에서 R1으로 가져옴)

ADD R3, R1, R4 (R1을 사용)

문제: LW는 데이터가 4단계(MEM)가 끝나야 나옵니다. ADD는 3단계(EX)에 데이터가 필요하죠. 시간을 거슬러 올라갈 수 없으므로, 무조건 1사이클을 쉬어야 합니다.

하드웨어 구현: Hazard Detection Unit이 이를 감지하면, PC와 IF/ID 레지스터가 업데이트되지 않도록 고정하고(Freeze), ID/EX 레지스터에 '0'(아무것도 하지 마라, No-op)을 주입합니다. 이것을 '버블(Bubble)을 넣는다'고 표현합니다.

2. Control Hazards

분기 명령어(beq, bne, jmp) 때문에 다음에 어떤 명령어를 가져올지 모를 때 발생합니다.

문제: beq R1, R2, Label 명령어는 2단계나 3단계가 되어야 점프 여부를 알 수 있습니다. 그동안 CPU는 이미 다음 명령어들을 파이프라인에 집어넣어 버립니다. 만약 점프를 해야 한다면, 이미 들어온 명령어들은 잘못된 것입니다.

해결책:

Flush (지우기): 예측이 틀렸을 때 파이프라인에 이미 들어온 명령어들을 싹 지웁니다. (성능 손해 막심)

Dynamic Branch Prediction (석사 핵심): 하드웨어가 과거 기록을 보고 "이 if문은 지난 10번 동안 9번 성공했으니 이번에도 성공할 거야"라고 예측합니다. BHT(Branch History Table) 같은 복잡한 하드웨어를 사용합니다.

3. Structural Hazards

두 명령어가 동시에 같은 하드웨어 자원을 쓰려고 할 때 발생합니다.

예시: 명령어 메모리와 데이터 메모리가 하나뿐인 경우. 1번 명령어가 데이터를 읽을 때(MEM), 4번 명령어가 다음 명령어를 가져오려고(IF) 하면 충돌합니다.

해결: 하드웨어를 분리합니다. (L1 Instruction Cache와 L1 Data Cache를 따로 둠)

💡 석사 수업을 위한 'Pre-Advanced' 지식
석사 과정에서는 여기서 더 나갑니다. 위 방식들은 모두 In-Order(순차적) 처리입니다. 교수님은 이제 이걸 가르치실 겁니다.

Scoreboarding / Tomasulo Algorithm: "데이터 의존성이 없는 뒷 명령어부터 먼저 실행해버리자!" (Out-of-Order Execution)

Register Renaming: "R1이라는 이름 때문에 충돌이 나네? 그럼 하드웨어가 이름을 잠깐 바꿔서 해결하자."

Out-of-Order Execution, OoO

학부 수준의 'In-Order(순차 실행)'를 넘어, 현대 CPU가 실제로 어떻게 수십 개의 명령어를 동시에 주무르는지 결정하는 비순차 실행(Out-of-Order Execution, OoO)의 핵심 기술들입니다.

이 기술들의 공통 목표는 하나입니다: "앞의 명령어가 (메모리 로딩 등으로) 멈춰있을 때, 뒤에 있는 상관없는 명령어라도 먼저 실행해서 노는 시간을 없애자!"

1. Register Renaming (레지스터 리네이밍)

비순차 실행을 하려고 보니, 하드웨어 성능을 발목 잡는 '가짜 의존성'이 발견되었습니다.

문제 상황:

ADD R1, R2, R3
SUB R1, R4, R5

여기서 2번의 R1은 1번과 아무 상관이 없습니다. 그냥 '이름'만 같은 저장소를 쓸 뿐이죠. 하지만 순서를 바꾸려다 보면 2번이 먼저 실행되어 1번의 결과값을 덮어버리는 대참사(WAW 해저드)가 날 수 있습니다.

해결책: CPU 내부에 실제 레지스터(Physical Register)를 엄청나게 많이(예: 128개) 만들어 둡니다.

소프트웨어상의 이름은 둘 다 R1이지만, 하드웨어가 내부적으로 1번은 P10에, 2번은 P25에 저장하도록 이름을 바꿔버립니다.
이렇게 하면 두 명령어는 서로 남남이 되어 동시에 실행될 수 있습니다

2. Scoreboarding (스코어보딩)

1960년대 CDC 6600에서 처음 등장한 비순차 실행 제어 방식입니다. 중앙 집중식 관제탑(Scoreboard)이 모든 자원과 데이터의 상태를 체크합니다.

동작 원리:

Issue: 명령어에 필요한 하드웨어 자원이 비어있으면 일단 보냅니다.

Read Operands: 모든 재료(데이터)가 준비될 때까지 관제탑이 체크하다가, 준비되면 실행을 허가합니다.

Execution: 실제 연산을 수행합니다.

Write Result: 결과값이 쓰일 레지스터를 기다리는 다른 명령어가 없는지 확인 후 저장합니다.

한계: 데이터가 준비될 때까지 기다리는 방식이라, 구조적 해저드와 데이터 해저드에 여전히 취약합니다. 특히 위에서 말한 '이름 중복(WAW, WAR)' 문제를 완벽히 해결하지 못해 멈추는(Stall) 경우가 많습니다.

3. Tomasulo Algorithm (토마술로 알고리즘) - 석사의 꽃

IBM의 로버트 토마술로가 고안한 방식으로, 현대 Intel/AMD CPU의 조상님 격인 알고리즘입니다. 스코어보딩 + 레지스터 리네이밍의 진화형입니다.

핵심 개념: Reservation Station (예약 스테이션)
관제탑 하나가 관리하는 게 아니라, 각 연산기(ALU, 부동소수점 연산기 등) 앞에 '대기실(Reservation Station)'을 둡니다.

동작 과정:

Issue: 명령어가 들어오면 대기실로 보냅니다. 이때 레지스터 리네이밍이 동시에 일어납니다. ("너는 이제부터 R1이 아니라 'Tag 7'번 결과값을 기다려라")

Execute: 대기실에 앉아 있다가, 내가 기다리던 데이터가 전광판에 뜨면 낚아채서 즉시 실행합니다.

Write Result (CDB): 결과가 나오면 CDB(Common Data Bus)라는 공용 전광판에 "나 7번인데 결과값 100이다!"라고 뿌립니다. 그럼 이걸 기다리던 다른 대기실 명령어들이 동시에 데이터를 받아갑니다.

왜 대단한가?

전광판(CDB) 시스템 덕분에 데이터가 나오자마자 필요한 모든 곳으로 동시에 퍼집니다.

하드웨어가 데이터의 흐름에 따라 스스로 스케줄링을 하므로, 컴파일러가 최적화를 덜 해도 CPU가 알아서 최고 속도를 냅니다.

profile
RTL, FPGA Engineer

0개의 댓글