파이프라이닝 (Pipelining)
핵심 인사이트 (3줄 요약)
명령어 실행을 여러 단계로 분리하여 병렬 처리하는 기법으로, 한 클럭에 한 명령어가 완료되는 효과(이상적 CPI=1)를 얻는다. 데이터 해저드, 제어 해저드, 구조 해저드의 문제를 포워딩, 분기 예측, 자원 복제로 해결해야 한다. 현대 CPU는 15~20단계의 슈퍼파이프라인과 슈퍼스칼라 구조로 IPC 4~8을 달성한다.
Ⅰ. 개요 (필수: 200자 이상)
개념: 파이프라이닝(Pipelining)은 CPU 명령어 실행 과정을 여러 개의 독립적인 단계(Stage)로 분리하고, 각 단계가 서로 다른 명령어를 동시에 처리하도록 하여 처리량(Throughput)을 극대화하는 기법이다.
💡 비유: 파이프라이닝은 **"세탁소 컨베이어 벨트"**와 같다. 한 사람이 빨래를 다 접고 나서야 다음 사람이 빨래를 시작하면 느리다. 대신 분류→세탁→건조→다림질→포장 각 단계에 직원을 배치하면, 한 번에 여러 빨래가 동시에 처리된다. 5단계 파이프라인에서는 이론적으로 5배 더 많은 빨래를 처리할 수 있다.
등장 배경 (필수: 3가지 이상 기술):
- 기존 문제점 - 단일 사이클 비효율: 단일 사이클 CPU는 모든 명령어가 가장 긴 명령어(메모리 접근)의 실행 시간에 맞춰 클럭이 설정되어, 짧은 명령어(ADD)도 긴 시간을 낭비했다. 다중 사이클 CPU는 명령어별로 다른 클럭 수를 사용했지만 여전히 순차 실행만 가능했다.
- 기술적 필요성: 명령어 실행 단계(IF, ID, EX, MEM, WB)가 서로 독립적인 하드웨어(PC, 레지스터, ALU, 메모리)를 사용한다는 점을 활용하면, 동시에 여러 명령어의 서로 다른 단계를 병렬 처리할 수 있다.
- 시장/산업 요구: 소프트웨어의 복잡도 증가와 실시간 처리 요구사항으로 인해, 클럭 속도 향상만으로는 한계가 있어 아키텍처 수준의 병렬성이 필요하게 되었다.
핵심 목적: 클럭당 완료되는 명령어 수인 IPC(Instructions Per Cycle)를 높여, 동일 클럭 속도에서 처리량을 획기적으로 향상시키는 것이다.
Ⅱ. 구성 요소 및 핵심 원리 (필수: 가장 상세하게)
구성 요소 (필수: 최소 4개 이상):
| 구성 요소 | 역할/기능 | 특징 | 비유 |
|---|---|---|---|
| IF (Instruction Fetch) | PC가 가리키는 명령어를 메모리에서 인출 | PC 갱신, 명령어 캐시 활용 | 주문 받기 |
| ID (Instruction Decode) | 명령어 해석, 레지스터 값 읽기 | 제어 신호 생성, 데이터 준비 | 레시피 확인 |
| EX (Execute) | ALU로 산술/논리 연산 수행 | 주소 계산, 분기 목적지 계산 | 요리 조리 |
| MEM (Memory Access) | 메모리에서 데이터 로드/스토어 | 데이터 캐시 활용 | 재료 추가 |
| WB (Write Back) | 결과를 목적지 레지스터에 저장 | 레지스터 파일 쓰기 | 플레이팅 |
| 파이프라인 레지스터 | 단계 간 데이터 전달용 임시 저장 | IF/ID, ID/EX, EX/MEM, MEM/WB | 컨베이어 벨트 |
| 해저드 검출 유닛 | 데이터/제어 의존성 탐지 | 스톨 및 포워딩 제어 | 품질 관리자 |
| 분기 예측기 | 분기 방향 예측 | BHT, BTB, 2-bit 예측 | 경험 많은 요리사 |
구조 다이어그램 (필수: ASCII 아트):
┌─────────────────────────────────────────────────────────────────────────────┐
│ 5단계 MIPS 파이프라인 구조 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 클럭: 1 2 3 4 5 6 7 8 │
│ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │
│ I1: │ IF │ ID │ EX │ MEM │ WB │ │ │ │ │
│ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ │
│ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │
│ I2: │ IF │ ID │ EX │ MEM │ WB │ │ │ │ │
│ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ │
│ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │
│ I3: │ IF │ ID │ EX │ MEM │ WB │ │ │ │ │
│ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ │
│ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │
│ I4: │ IF │ ID │ EX │ MEM │ WB │ │ │ │
│ └─────┴─────┴─────┴─────┴─────┴─────┴─────┘ │
│ ┌─────┬─────┬─────┬─────┬─────┬─────┐ │
│ I5: │ IF │ ID │ EX │ MEM │ WB │ │ │
│ └─────┴─────┴─────┴─────┴─────┴─────┘ │
│ │
│ ←─────── 5사이클 후 매 사이클마다 1개 명령어 완료 ───────→ │
│ │
├─────────────────────────────────────────────────────────────────────────────┤
│ 파이프라인 하드웨어 구조 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ IF │───→│ ID │───→│ EX │───→│ MEM │───→│ WB │ │
│ │ 명령어 │ │ 명령어 │ │ 실행 │ │ 메모리 │ │ 레지스터│ │
│ │ 인출 │ │ 해석 │ │ │ │ 접근 │ │ 쓰기 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ ↑ ↑ ↑ ↑ ↑ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │IF/ID │ │ID/EX │ │EX/MEM │ │MEM/WB │ │ │ │
│ │레지스터 │ │레지스터 │ │레지스터 │ │레지스터 │ │ │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │ │
│ ↓ ↓ ↓ ↓ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ PC │ │레지스터 │ │ ALU │ │ 데이터 │ │
│ │ 명령어 │ │ 파일 │ │ │ │ 메모리 │ │
│ │ 캐시 │ │ 제어유닛│ │ │ │ 캐시 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
동작 원리 (필수: 단계별 상세 설명):
① IF(인출) → ② ID(해석) → ③ EX(실행) → ④ MEM(메모리) → ⑤ WB(쓰기)
-
1단계 - IF (Instruction Fetch, 명령어 인출):
- PC(Program Counter) 값을 주소로 명령어 메모리 접근
- 명령어 캐시에서 명령어 인출 (32bit/64bit)
- PC 갱신: PC = PC + 4 (또는 분기 목적지)
- IF/ID 파이프라인 레지스터에 명령어와 PC+4 저장
-
2단계 - ID (Instruction Decode, 명령어 해석):
- 명령어의 Opcode 필드 해석 → 제어 신호 생성
- 레지스터 파일에서 Source 레지스터 값 읽기
- Sign-Extend로 즉치값(Immediate) 확장
- ID/EX 파이프라인 레지스터에 데이터 전달
-
3단계 - EX (Execute, 실행):
- ALU로 산술/논리 연산 수행
- 메모리 주소 계산 (Base + Offset)
- 분기 조건 평가 및 목적지 계산
- EX/MEM 파이프라인 레지스터에 결과 저장
-
4단계 - MEM (Memory Access, 메모리 접근):
- Load: 데이터 캐시에서 메모리 값 읽기
- Store: 데이터 캐시에 값 쓰기
- 산술/논리 명령어는 이 단계를 통과만 함
- MEM/WB 파이프라인 레지스터에 결과 전달
-
5단계 - WB (Write Back, 레지스터 쓰기):
- ALU 결과 또는 메모리에서 읽은 데이터를 목적지 레지스터에 저장
- 레지스터 파일의 Write 포트 활용
- 명령어 완료, 다음 명령어가 WB 단계에 진입
핵심 알고리즘/공식 (해당 시 필수):
파이프라인 성능 공식:
┌─────────────────────────────────────────────────────────────────┐
│ Speedup = n × t_nonpipelined / [(n + k - 1) × t_pipelined] │
│ │
│ n: 명령어 수 │
│ k: 파이프라인 단계 수 │
│ t: 클럭 주기 │
│ │
│ 이상적 (n → ∞): Speedup = k │
│ 5단계 파이프라인 → 최대 5배 향상 │
├─────────────────────────────────────────────────────────────────┤
│ CPI (Cycles Per Instruction): │
│ CPI_ideal = 1 (모든 단계 1 클럭씩) │
│ CPI_actual = 1 + Stall_Cycles_Per_Instruction │
│ │
│ 예: 20% 분기, 2사이클 스톨 → CPI = 1 + 0.2×2 = 1.4 │
└─────────────────────────────────────────────────────────────────┘
파이프라인 효율:
Efficiency = (실제 처리 명령어 수) / (최대 처리 가능 명령어 수)
= n / (n + k - 1)
예: 100개 명령어, 5단계
Efficiency = 100 / 104 = 96.2%
코드 예시 (필수: Python 또는 의사코드):
# 파이프라인 시뮬레이터
class PipelineSimulator:
def __init__(self):
# 파이프라인 레지스터
self.if_id = {'ir': None, 'pc': 0} # IF/ID
self.id_ex = {'a': 0, 'b': 0, 'imm': 0, # ID/EX
'op': None, 'rd': 0}
self.ex_mem = {'result': 0, 'md': 0, # EX/MEM
'op': None, 'rd': 0}
self.mem_wb = {'result': 0, 'md': 0, # MEM/WB
'op': None, 'rd': 0}
# 하드웨어 자원
self.pc = 0
self.registers = [0] * 32
self.memory = [0] * 1024
self.instructions = []
# 통계
self.cycles = 0
self.stalls = 0
self.branch_mispredictions = 0
def load_program(self, instructions):
"""프로그램 로드"""
self.instructions = instructions
self.pc = 0
def step(self):
"""1 클럭 사이클 실행"""
self.cycles += 1
# 5단계 동시 실행 (역순으로 처리)
self._writeback()
self._memory()
self._execute()
self._decode()
self._fetch()
def _fetch(self):
"""IF 단계"""
if self.pc < len(self.instructions):
self.if_id['ir'] = self.instructions[self.pc]
self.if_id['pc'] = self.pc
self.pc += 1
def _decode(self):
"""ID 단계"""
if self.if_id['ir'] is not None:
instr = self.if_id['ir']
op, rd, rs, rt, imm = self._parse_instruction(instr)
self.id_ex['op'] = op
self.id_ex['rd'] = rd
self.id_ex['a'] = self.registers[rs]
self.id_ex['b'] = self.registers[rt]
self.id_ex['imm'] = imm
def _execute(self):
"""EX 단계"""
if self.id_ex['op'] is not None:
op = self.id_ex['op']
if op == 'ADD':
self.ex_mem['result'] = self.id_ex['a'] + self.id_ex['b']
elif op == 'SUB':
self.ex_mem['result'] = self.id_ex['a'] - self.id_ex['b']
elif op == 'ADDI':
self.ex_mem['result'] = self.id_ex['a'] + self.id_ex['imm']
elif op == 'LW':
self.ex_mem['result'] = self.id_ex['a'] + self.id_ex['imm']
elif op == 'SW':
self.ex_mem['result'] = self.id_ex['a'] + self.id_ex['imm']
self.ex_mem['md'] = self.id_ex['b']
elif op == 'BEQ':
if self.id_ex['a'] == self.id_ex['b']:
self.pc = self.id_ex['imm'] # 분기
self.ex_mem['op'] = op
self.ex_mem['rd'] = self.id_ex['rd']
def _memory(self):
"""MEM 단계"""
if self.ex_mem['op'] is not None:
op = self.ex_mem['op']
if op == 'LW':
self.mem_wb['md'] = self.memory[self.ex_mem['result']]
elif op == 'SW':
self.memory[self.ex_mem['result']] = self.ex_mem['md']
self.mem_wb['op'] = op
self.mem_wb['rd'] = self.ex_mem['rd']
self.mem_wb['result'] = self.ex_mem['result']
def _writeback(self):
"""WB 단계"""
if self.mem_wb['op'] is not None:
op = self.mem_wb['op']
if op in ['ADD', 'SUB', 'ADDI', 'LW']:
if op == 'LW':
self.registers[self.mem_wb['rd']] = self.mem_wb['md']
else:
self.registers[self.mem_wb['rd']] = self.mem_wb['result']
def _parse_instruction(self, instr):
"""명령어 파싱"""
parts = instr.replace(',', ' ').split()
op = parts[0]
if op in ['ADD', 'SUB']:
rd = int(parts[1][1:])
rs = int(parts[2][1:])
rt = int(parts[3][1:])
return op, rd, rs, rt, 0
elif op == 'ADDI':
rd = int(parts[1][1:])
rs = int(parts[2][1:])
imm = int(parts[3])
return op, rd, rs, 0, imm
elif op == 'LW':
rd = int(parts[1][1:])
return op, rd, 0, 0, 0
return op, 0, 0, 0, 0
def run(self, max_cycles=100):
"""프로그램 실행"""
for _ in range(max_cycles):
self.step()
if self.pc >= len(self.instructions) and self._pipeline_empty():
break
return self.cycles
def _pipeline_empty(self):
return (self.if_id['ir'] is None and
self.id_ex['op'] is None and
self.ex_mem['op'] is None and
self.mem_wb['op'] is None)
# 사용 예시
sim = PipelineSimulator()
program = [
'ADD R1, R2, R3', # R1 = R2 + R3
'ADD R4, R1, R5', # R4 = R1 + R5 (데이터 해저드!)
'ADDI R6, R6, 1', # R6 = R6 + 1
'ADD R7, R8, R9', # R7 = R8 + R9
]
sim.load_program(program)
sim.registers[2] = 10
sim.registers[3] = 20
sim.registers[5] = 5
cycles = sim.run()
print(f"실행 완료: {cycles} 클럭 사이클")
print(f"R1 = {sim.registers[1]}") # 30
print(f"R4 = {sim.registers[4]}") # 35
Ⅲ. 기술 비교 분석 (필수: 2개 이상의 표)
장단점 분석 (필수: 최소 3개씩):
| 장점 | 단점 |
|---|---|
| 처리량 향상: 이상적 IPC = 1 (클럭당 1명령어) | 해저드 처리 복잡: 데이터/제어/구조 의존성 |
| 하드웨어 효율: 각 단계 전담 하드웨어 활용 | 분기 비용: 예측 실패 시 파이프라인 플러시 |
| 높은 클럭 가능: 각 단계가 단순해짐 | 설계 복잡도: 포워딩, 예측 로직 필요 |
| 투명성: 프로그래머가 인지할 필요 없음 | 파이프라인 플러시 비용: 10~20 사이클 손실 |
파이프라인 해저드 유형 및 해결책 비교 (필수: 최소 2개 대안):
| 해저드 유형 | 원인 | 문제 상황 | 해결 방법 | 패널티 |
|---|---|---|---|---|
| 데이터 해저드 (Data) | 이전 명령어 결과를 다음 명령어가 사용 | ADD R1,R2,R3 → SUB R4,R1,R5 | 포워딩(Forwarding), 스톨, 재배치 | 0~3 cycle |
| 제어 해저드 (Control) | 분기 명령어로 PC 변경 | BEQ 후 다음 명령어 실행 여부 | 분기 예측, 지연 슬롯, 스톨 | 1~20 cycle |
| 구조 해저드 (Structural) | 동시에 같은 자원 사용 | 두 명령어가 동시 메모리 접근 | 자원 복제, 하버드 구조 | 1~2 cycle |
★ 선택 기준:
- 데이터 해저드: 포워딩을 기본으로, 해결 안 되면 스톨
- 제어 해저드: 동적 분기 예측(90%+ 정확도)으로 미스 최소화
- 구조 해저드: 하버드 구조(명령어/데이터 메모리 분리)로 사전 차단
분기 예측 기법 비교:
| 기법 | 원리 | 정확도 | 하드웨어 비용 | 사용처 |
|---|---|---|---|---|
| 정적 예측 (Always Not Taken) | 분기 안 함으로 가정 | 50% | 없음 | 초기 CPU |
| 정적 예측 (Backward Taken) | 뒤로 분기면 루프→분기 | 65% | 없음 | 간단한 시스템 |
| 1-bit 예측 | 직전 결과 사용 | 80% | 매우 낮음 | 임베디드 |
| 2-bit 예측 | 상태 머신(Strong/Weak) | 90% | 낮음 | ★ 실제 CPU |
| BHT (Branch History Table) | PC 기반 이력 저장 | 93% | 중간 | ★ 현대 CPU |
| 2-level 예측 | 전역+지역 이력 조합 | 97% | 높음 | 고성능 CPU |
| 신경망 예측 | Perceptron 기반 | 99%+ | 매우 높음 | 최신 CPU |
Ⅳ. 실무 적용 방안 (필수: 기술사 판단력 증명)
기술사적 판단 (필수: 3개 이상 시나리오):
| 적용 분야 | 구체적 적용 방법 | 기대 효과 (정량) |
|---|---|---|
| CPU 아키텍처 설계 | 슈퍼스칼라(4-way issue) + 비순차 실행 + 15단계 파이프라인 | IPC 4~6 달성 |
| 임베디드 시스템 | ARM Cortex-M 3단계 파이프라인, 저전력 최적화 | 전력 대비 성능 최적화 |
| 컴파일러 최적화 | 루프 언롤링, 명령어 스케줄링으로 해저드 회피 | CPI 20~40% 개선 |
| 실시간 시스템 | WCET(Worst Case Execution Time) 분석 시 파이프라인 영향 고려 | 보수적 예측으로 데드라인 보장 |
실제 도입 사례 (필수: 구체적 기업/서비스):
- 사례 1: 인텔 Core 아키텍처 - 14~19단계 파이프라인, 4-way 슈퍼스칼라, 분기 예측 정확도 97%+ 달성. 비순차 실행으로 IPC 4 이상.
- 사례 2: ARM Cortex-A78 - 11단계 파이프라인, 4-way issue, 모바일용 저전력 설계. 3GHz 동작으로 AI 추론 가속.
- 사례 3: Apple M2 - 10단계 파이프라인, 8-wide decode, 128개 재정렬 버퍼. IPC 6~8 달성으로 x86 대비 동전력 2배 성능.
도입 시 고려사항 (필수: 4가지 관점):
-
기술적:
- 파이프라인 단계 수 결정 (깊을수록 높은 클럭, 해저드 패널티 증가)
- 슈퍼스칼라 설계 (Issue Width, Reservation Station)
- 비순차 실행 범위 (Reorder Buffer 크기)
- 분기 예측 정확도 목표
-
운영적:
- 컴파일러 지원 (명령어 스케줄링, 루프 최적화)
- 디버깅 복잡도 (파이프라인 상태 추적 어려움)
- 성능 프로파일링 (해저드 빈도 분석)
- 검증 및 테스트 (모든 파이프라인 조합 테스트)
-
보안적:
- Spectre 공격: 투기적 실행 악용
- 파이프라인 사이드 채널: 타이밍 정보 유출
- 추측 실행 버퍼 오버플로우
- 완화: Retpoline, KPTI
-
경제적:
- 설계 복잡도 증가 → 개발 비용 상승
- 검증 비용 (파이프라인 버그는 치명적)
- 클럭 속도 vs 전력 소비 트레이드오프
- 성능 대비 수확 체감 (단계 수 무한 증가 불가)
주의사항 / 흔한 실수 (필수: 최소 3개):
- ❌ 파이프라인 너무 깊게 설계: Intel NetBurst (31단계)는 높은 클럭이지만 분기 미스 페널티 과대 → 실패
- ❌ 분기 예측 무시: 분기 미스 1회당 10~20 사이클 손실, 전체 성능 20~30% 저하 가능
- ❌ 데이터 의존성 간과: 컴파일러가 스케줄링 안 하면 포워딩으로도 해결 안 되는 경우 존재
관련 개념 / 확장 학습 (필수: 최소 5개 이상 나열):
📌 파이프라이닝 핵심 연관 개념 맵
┌─────────────────────────────────────────────────────────────────┐
│ 파이프라이닝 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 해저드 ←──→ 파이프라인 ←──→ 슈퍼스칼라 │
│ ↓ ↓ ↓ │
│ 포워딩 명령어사이클 비순차실행 │
│ ↓ ↓ ↓ │
│ 분기예측 CPU구조 재정렬버퍼 │
│ │
└─────────────────────────────────────────────────────────────────┘
| 관련 개념 | 관계 | 설명 | 문서 링크 |
|---|---|---|---|
| 해저드 (Hazard) | 핵심 문제 | 데이터/제어/구조 의존성으로 파이프라인 중단 | [해저드](./hazard.md) |
| 명령어 사이클 | 기반 개념 | IF-ID-EX-MEM-WB 5단계 구성 | [명령어 사이클](./instruction_cycle.md) |
| CPU 구조 | 하드웨어 기반 | 파이프라인 레지스터, ALU, 메모리 | [CPU](./cpu.md) |
| 슈퍼스칼라 | 확장 기술 | 여러 명령어 동시 발송 (Multi-issue) | [슈퍼스칼라](./superscalar.md) |
| 분기 예측 | 제어 해저드 해결 | BHT, BTB로 분기 방향 예측 | [분기 예측](./branch_prediction.md) |
| 비순차 실행 | 성능 향상 | 의존성 없는 명령어 먼저 실행 | [비순차 실행](./out_of_order.md) |
| 캐시 메모리 | 메모리 계층 | 캐시 미스 시 파이프라인 스톨 | [캐시 메모리](./cache_memory.md) |
Ⅴ. 기대 효과 및 결론 (필수: 미래 전망 포함)
정량적 기대 효과 (필수):
| 효과 영역 | 구체적 내용 | 정량적 목표 |
|---|---|---|
| 처리량 | 클럭당 완료 명령어 수 증가 | 단일 사이클 대비 4~8배 향상 |
| 클럭 속도 | 각 단계 단순화로 높은 클럭 | 3~5GHz 이상 동작 |
| 전력 효율 | 스톨 감소로 유휴 전력 절감 | 동성능 대비 전력 30% 절감 |
| 응답성 | 높은 IPC로 실행 시간 단축 | 응답 지연 50% 이상 감소 |
미래 전망 (필수: 3가지 관점):
-
기술 발전 방향:
- VLIW/EPIC: 컴파일러가 명령어 병렬성 결정 (Itanium 시도, 한계)
- Systolic Array: AI 가속기용 파이프라인 (TPU, 데이터 흐름)
- 에너지 효율 파이프라인: Clock Gating, Power Gating 강화
-
시장 트렌드:
- AI 추론용 단순 파이프라인 (RISC-V, ARM)
- 이종 코어 파이프라인 (big.LITTLE, DynamIQ)
- 실시간 시스템용 예측 가능 파이프라인
-
후속 기술:
- 양자 파이프라인: 양자 게이트 병렬 실행
- 광(Photonics) 파이프라인: 광 인터커넥트로 지연 최소화
- 뉴로모픽 파이프라인: 스파이킹 신경망 기반 처리
결론: 파이프라이닝은 현대 CPU 성능의 핵심 기반 기술로, 해저드 관리의 정교함이 실제 성능을 결정한다. 슈퍼스칼라, 비순차 실행, 분기 예측과 결합하여 IPC 4~8을 달성하며, AI 가속기의 Systolic Array로도 진화하고 있다.
※ 참고 표준: Patterson & Hennessy "Computer Organization and Design", MIPS32 Architecture, ARM Architecture Reference Manual, Intel 64 SDM
어린이를 위한 종합 설명 (필수)
파이프라이닝은 "피자 가게 컨베이어 벨트"야!
피자 가게에서 피자를 만들 때, 한 명이 처음부터 끝까지 다 하면 느려요. 도우를 펴고 → 소스를 바르고 → 토핑을 올리고 → 굽고 → 포장하는데 5분이 걸린다고 해봐요.
옛날 방식 (파이프라인 없음):
요리사 1: [피자1 도우][소스][토핑][굽기][포장] (5분)
요리사 1: [피자2 도우][소스]...
→ 5분에 피자 1개밖에 못 만들어요! 😢
파이프라인 방식:
도우 담당: [피자1][피자2][피자3][피자4][피자5]
소스 담당: [피자1][피자2][피자3][피자4]
토핑 담당: [피자1][피자2][피자3]
굽기 담당: [피자1][피자2]
포장 담당: [피자1]
→ 5번째 분부터는 1분마다 피자 1개씩 완성! 🍕
문제점들 (해저드):
- 데이터 해저드: "치즈 다 썼어요!" → 옆 친구에게 바로 전달받기 (포워딩)
- 제어 해저드: "오늘은 쉬는 날인가?" → 미리 예측해서 준비 (분기 예측)
- 구조 해저드: "오븐이 하나예요!" → 오븐 두 개 설치 (자원 복제)
비밀: 요즘 컴퓨터(CPU)는 20단계 컨베이어 벨트를 사용해서 엄청나게 빨라요! 🏃💨