CPU 바운드 vs I/O 바운드
핵심 인사이트 (3줄 요약)
- 본질: 컴퓨터에서 실행되는 모든 프로세스는 그 성격에 따라, 연산에 시간을 쏟는 CPU 바운드(Bound) 프로세스와, 디스크나 네트워크 등 외부 장치를 기다리는 데 시간을 쏟는 I/O 바운드 프로세스로 나뉜다.
- 스케줄링의 딜레마: I/O 바운드 프로세스는 응답 속도(Interactive)가 중요하므로 깨어날 때마다 CPU를 즉시 줘야 하지만 한 번 잡으면 금방 놓는다. 반면 CPU 바운드는 한 번 CPU를 잡으면 할당 시간(Time Quantum)을 꽉 채워서 다른 앱들을 버벅거리게 만든다.
- 가치/튜닝: 시스템 아키텍트는 서버가 터졌을 때 "이 워크로드가 CPU 바운드인가, I/O 바운드인가?"를 정확히 진단해야 한다. 원인에 따라 CPU 클럭을 높일지(Scale-up), 스레드 풀을 늘리거나 비동기로 뺄지(Scale-out/Async) 처방전이 180도 달라지기 때문이다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념:
- CPU Burst (버스트): 프로세스가 CPU를 잡고 미친 듯이 연산만 수행하는 연속적인 시간 구간.
- I/O Burst: 프로세스가 디스크에서 파일을 읽거나 네트워크 패킷을 기다리며 대기(Block/Sleep)하는 시간 구간.
- 프로세스의 일생은 [CPU 버스트 $\rightarrow$ I/O 버스트 $\rightarrow$ CPU 버스트 $\rightarrow$ I/O 버스트]의 끝없는 반복이다.
-
CPU 바운드 프로세스:
- I/O 버스트보다 CPU 버스트가 압도적으로 긴 프로그램.
- 예: 동영상 인코딩, 3D 렌더링, 머신러닝 학습, 비트코인 채굴, 무한 루프.
-
I/O 바운드 프로세스:
- CPU 버스트는 아주 짧고, I/O 버스트(대기 시간)가 압도적으로 긴 프로그램.
- 예: 카카오톡(사용자 키보드 대기), 웹 브라우저, 웹 서버(DB와 네트워크 대기), 파일 복사 프로그램.
-
필요성 (스케줄러의 편애):
- OS 스케줄러가 두 프로세스를 똑같이 대우하면 시스템이 엉망이 된다. 동영상 인코딩(CPU 바운드)이 CPU를 꽉 잡고 안 놔주면, 사용자가 카카오톡(I/O 바운드)에 글자를 쳤을 때 3초 뒤에나 글자가 찍힌다(응답 지연).
- 해결책: OS는 불공평해야 한다. 사용자와 소통하는 I/O 바운드 프로세스의 우선순위를 강제로 높여주어(Boost), 이들이 키보드 입력을 받는 즉시 CPU를 새치기(Preempt)하게 만들어야 시스템이 부드럽게 돌아간다.
-
💡 비유:
- CPU 바운드 (수학자): 밥(I/O)도 안 먹고 방에 틀어박혀 하루 종일 칠판에 수학 공식만 푸는 사람. 방해하면 싫어함.
- I/O 바운드 (택배 상하차 직원): 1분 일하고(CPU), 트럭이 올 때까지 10분을 앉아서 쉰다(I/O). 대신 트럭이 오면 즉시 벌떡 일어나서 상자를 내려야 한다.
- OS의 역할: 트럭이 도착했을 때, 수학자를 잠깐 밀어내고 상하차 직원이 바로 일할 수 있게 자리를 마련해 준다.
-
📢 섹션 요약 비유: CPU 바운드는 마라톤 선수고 I/O 바운드는 100m 단거리 선수입니다. 트랙(CPU)을 관리하는 심판(OS)은 단거리 선수가 뛰고 싶어 할 땐 즉시 트랙을 비워주고, 그가 쉴 때 마라톤 선수가 오랫동안 트랙을 쓰게 해주는 지혜를 발휘해야 합니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
CPU Burst Cycle의 통계적 분포
운영체제 학자들이 전 세계의 수많은 프로그램을 분석해 보니, CPU 버스트 길이는 지수 분포(Exponential Distribution)를 따랐다.
┌───────────────────────────────────────────────────────────────────┐
│ CPU Burst 길이의 확률 분포 그래프 │
├───────────────────────────────────────────────────────────────────┤
│ │
│ 빈도수(빈번함) │
│ ▲ │
│ │ * (짧은 CPU Burst가 압도적으로 많음 = I/O Bound) │
│ │ * * │
│ │ * * │
│ │ * * │
│ │ * * * │
│ │ * * * * * (긴 CPU Burst는 매우 드묾 = CPU Bound)│
│ │ * * * * * * * * * * * * │
│ └─────────────────────────────────────────────────────────▶ │
│ 1ms 10ms 50ms 1000ms CPU 시간 │
└───────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 세상에 존재하는 90%의 프로그램은 아주 잠깐(1ms) 연산하고 I/O를 기다리러 떠난다(I/O Bound). 반면, 한 번 CPU를 잡으면 수백 ms 동안 안 놓는 무거운 프로그램(CPU Bound)은 극소수다. 스케줄러의 목표는 이 다수의 I/O 바운드 프로세스들이 "응답 지연"을 느끼지 않게, 소수의 CPU 바운드 프로세스로부터 지켜내는 것이다.
스케줄러의 I/O 바운드 판별 및 보상 메커니즘
OS 스케줄러(CFS나 MLFQ)는 이 프로세스가 CPU 바운드인지 I/O 바운드인지 처음에는 모른다. 과거의 행동(History)을 보고 판단한다.
- 감시: 프로세스 A에게 타임 슬라이스(예: 10ms)를 줬다.
- I/O Bound 판별: 프로세스 A가 1ms만 쓰고 "나 디스크 읽을게" 하고 스스로 CPU를 반납(Sleep)했다.
- OS의 판단: "아, 얘는 I/O 바운드구나! 착하네."
- 조치: 우선순위 승격 (Priority Boost). 나중에 디스크 읽기가 끝나고 깨어났을 때, 남들 다 제치고 1순위로 CPU를 주어 응답 시간을 극대화한다.
- CPU Bound 판별: 프로세스 B에게 10ms를 줬더니, 시간을 끝까지 다 채워서 OS가 강제로 뺏을 때까지 연산만 했다.
- OS의 판단: "너는 CPU 바운드(욕심쟁이)구나."
- 조치: 우선순위 강등 (Penalty). 대신 한 번 CPU를 잡았을 때 길게 쓰도록 타임 퀀텀을 넉넉히(예: 50ms) 주어 문맥 교환 오버헤드라도 줄여준다.
- 📢 섹션 요약 비유: 뷔페(CPU)에서 한 접시만 뚝딱 먹고 나가는 손님(I/O 바운드)에게는 다음번에 오면 프리패스로 줄을 안 서게 해주고, 하루 종일 앉아서 먹는 손님(CPU 바운드)은 구석 자리로 몰아서 시간제한을 두는 것이 OS의 합리적 차별입니다.
Ⅲ. 융합 비교 및 다각도 분석
CPU Bound vs I/O Bound 성능 튜닝 전략
서버가 느려졌을 때, 병목의 원인에 따라 처방은 완전히 다르다.
| 구분 | CPU Bound 워크로드 | I/O Bound 워크로드 |
|---|---|---|
| 병목 지점 | CPU 연산력 (클럭, 코어 수) | 디스크 속도, 네트워크 지연, DB 락(Lock) |
| 스레드 풀 크기 | 코어 수와 동일하게 (예: 16코어 = 16개) | 코어 수의 수십 배 (예: 16코어 = 200개) |
| 다중 프로그래밍 | 오히려 낮춰야 함 (스위칭 오버헤드 방지) | 무조건 높여야 함 (CPU가 쉬는 걸 막기 위해) |
| 스케일링 전략 | Scale-Up (고성능 CPU로 교체) | Scale-Out (서버 대수 늘리기), 비동기 I/O 전환 |
| 적용 사례 | 암호화 화폐 채굴, 이미지 딥러닝 | 웹 서버(Nginx), API 게이트웨이, DB 서버 |
과목 융합 관점
-
소프트웨어공학 (SE): Node.js나 Python의 asyncio 같은 '단일 스레드 비동기 이벤트 루프' 모델은 철저하게 I/O 바운드 워크로드에 특화된 아키텍처다. I/O 대기 시간에 다른 요청을 처리해 효율이 극강이다. 하지만 이 서버에 CPU 바운드(예: 1GB JSON 파싱) 요청이 하나라도 들어오면, 이벤트 루프 자체가 블로킹되어 수만 명의 다른 I/O 요청이 전부 멈춰버리는 대참사가 터진다.
-
클라우드 컴퓨팅 (Cloud): AWS EC2 인스턴스를 고를 때, C 계열(Compute Optimized)은 CPU 바운드 연산용이고, I 계열(I/O Optimized)이나 M 계열(General)은 I/O 바운드에 적합하다. 앱의 바운드 성향을 모른 채 아무 인스턴스나 띄우면 매달 수천만 원의 클라우드 요금이 낭비된다.
-
📢 섹션 요약 비유: 화물차(서버)가 느리다고 무작정 엔진(CPU)을 바꾸면 안 됩니다. 짐이 무거워서(CPU Bound) 느린 거면 엔진을 바꿔야 하지만, 상하차 알바생이 느려서(I/O Bound) 차가 못 떠나는 거라면 알바생을 더 고용(스레드 풀 증가)해야 합니다.
Ⅳ. 실무 적용 및 기술사적 판단
실무 시나리오
-
시나리오 — 톰캣(Tomcat) 스레드 풀 튜닝 실패로 인한 스래싱: 16코어 서버에서 돌아가는 자바 스프링 기반 API 서버(I/O Bound). 초보 개발자가 "성능을 높이겠다"며 스레드 풀(MaxThreads)을 200에서 10,000으로 무작정 늘려버림.
- 원인 분석: I/O 바운드 앱은 스레드를 넉넉히 주는 게 맞다. 하지만 10,000개는 선을 넘었다. DB 병목으로 인해 10,000개의 스레드가 모두 I/O 대기에 빠졌다가 DB 응답이 오는 순간 동시에 깨어난다(Thundering Herd). CPU 코어 16개가 1만 개의 스레드를 문맥 교환(Context Switch)하느라 시스템 전체 CPU가 100%를 치고(Sys time 폭주), 정작 앱 연산(User time)은 0%가 되는 스래싱(Thrashing) 늪에 빠졌다.
- 대응 (기술사적 가이드): Little's Law 등 대기열 이론을 바탕으로 적정 스레드 풀을 산정해야 한다. 이상적인 스레드 풀 공식:
Threads = CPU 코어 수 * (1 + (I/O 대기 시간 / CPU 처리 시간)). 만약 이 공식을 넘어서는 동시 접속(10K)을 처리해야 한다면, 전통적인 톰캣 멀티스레드 모델을 버리고 Netty나 Spring WebFlux 같은 비동기(Non-blocking I/O) 프레임워크로 아키텍처를 전면 전환해야 한다.
-
시나리오 — 배달 앱의 머신러닝 추천 API 응답 지연 (CPU Bound 침범): 배달 앱 홈 화면을 띄울 때 사용자에게 맞는 가게를 딥러닝으로 추천해 주는 API를 호출한다. 이 API가 들어있는 마이크로서비스가 응답 시간(Latency)이 3초나 걸려 홈 화면이 마비됨.
- 원인 분석: 머신러닝 추론(Inference)은 완벽한 CPU 바운드 작업이다. 이 무거운 CPU 바운드 로직을, 일반적인 웹 라우팅과 DB 조회를 담당하는 I/O 바운드 노드(Pod)에 섞어서 올려놓았다. 머신러닝 연산이 CPU 타임 슬라이스를 길게 독점해 버리면서, 가벼운 I/O 요청들의 응답 시간이 다 같이 늦어진 것이다.
- 아키텍처 적용 (워크로드 격리): I/O 바운드 서비스와 CPU 바운드 서비스는 절대 같은 노드(또는 같은 스레드 풀)에 섞어 쓰면 안 된다. 머신러닝 추천 로직을 별도의 컨테이너로 분리(Decoupling)하여 GPU 인스턴스나 Compute 전용 노드 그룹으로 격리 스케줄링해야 한다.
의사결정 및 튜닝 플로우
┌───────────────────────────────────────────────────────────────────┐
│ 워크로드 특성(CPU vs I/O) 기반 아키텍처 의사결정 플로우 │
├───────────────────────────────────────────────────────────────────┤
│ │
│ [서버 성능 모니터링 툴(top, vmstat)을 통한 병목 확인] │
│ │ │
│ ▼ │
│ `top`에서 CPU의 `%us`(유저)가 90% 이상이고 `%wa`(I/O 대기)는 0인가? │
│ ├─ 예 ─────▶ [명확한 CPU Bound 워크로드] │
│ │ 대책 1: 로직 알고리즘 자체의 시간 복잡도(O(n)) 개선 │
│ │ 대책 2: Scale-Up (CPU 클럭/코어 수 높은 장비로 마이그레이션)│
│ └─ 아니오 │
│ │ │
│ ▼ │
│ CPU 사용률은 20% 이내인데, `%wa`(Wait)가 높거나 스레드들이 다 자고 있는가?│
│ ├─ 예 ─────▶ [명확한 I/O Bound 워크로드] │
│ │ 대책 1: I/O 병목 지점(DB 쿼리, 외부 API 네트워크) 튜닝│
│ │ 대책 2: 스레드 풀 증가 또는 비동기(Async) 아키텍처 적용 │
│ │ │
│ └─ 아니오 ──▶ 메모리 스왑(Swapping)이나 락(Lock) 경합 문제 의심 │
└───────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 초보자는 서버가 느리면 "서버 사양(CPU)을 올립시다"라고 한다. 하지만 I/O 바운드 병목(DB가 느림)일 때 웹 서버의 CPU를 100코어짜리로 바꿔봤자 속도는 1ms도 빨라지지 않는다. 기술사는 모니터링 지표를 보고 현재의 워크로드가 CPU 바운드인지 I/O 바운드인지 단번에 갈라내어 엉뚱한 곳에 돈을 쓰지 않도록 막는 사람이다.
도입 체크리스트
-
적응형 스케줄러 튜닝: 리눅스의 CFS 스케줄러는 이 두 가지 바운드를 스스로 판단하여 공평하게 조절한다. 하지만 백그라운드 영상 인코딩(CPU Bound) 때문에 마우스가 끊긴다면, 해당 인코더 프로세스의
nice값을 19로 낮추어(우선순위 강등) OS의 판단을 돕는 수동 튜닝을 거쳤는가? -
📢 섹션 요약 비유: CPU 바운드는 소화불량이고, I/O 바운드는 영양실조입니다. 소화가 안 돼서(CPU 과부하) 배가 아픈 환자에게 밥을 더 주거나, 영양실조(I/O 대기) 환자에게 소화제를 먹이는 오진을 피하는 것이 진단(모니터링)의 핵심입니다.
Ⅴ. 기대효과 및 결론
정량/정성 기대효과
| 구분 | 성향을 무시한 아키텍처 | CPU/I/O Bound 분리 및 맞춤 아키텍처 | 개선 효과 |
|---|---|---|---|
| 정량 (처리량) | I/O 병목으로 CPU 10% 사용 | 비동기 적용으로 CPU 80% 활용 | 동일 하드웨어 대비 TPS 5배 이상 증가 |
| 정량 (응답 시간) | CPU 로직 혼재로 화면 멈춤 발생 | 워크로드 격리로 즉각적 I/O 반응 | P99 지연 시간(Tail Latency) 대폭 감소 |
| 정성 (비용 효율) | 원인 모를 Scale-up으로 돈 낭비 | 병목 구간(DB, 네트워크) 정확히 타격 | 클라우드 인프라 유지보수 비용(TCO) 최적화 |
미래 전망
- 이기종(Heterogeneous) 프로세싱의 가속: CPU 바운드 연산 중에서도 단순 병렬 계산(행렬 곱 등)은 CPU가 하기엔 비효율적이다. 이런 극단적 CPU 바운드 연산을 GPU, NPU, TPU 같은 특수 목적 가속기로 덜어내어(Offloading), 메인 CPU는 순수하게 I/O 바운드와 스케줄링(제어)에만 집중하게 만드는 것이 현대 모바일 칩셋(Apple Silicon 등)과 클라우드 서버의 지배적인 발전 방향이다.
- eBPF를 이용한 실시간 워크로드 분류: 단순히 프로세스 단위로 I/O 성향을 나누는 것을 넘어, 패킷 단위나 시스템 콜 단위로 eBPF가 실시간 프로파일링을 수행하여, "현재 이 컨테이너는 10초간 CPU 바운드 모드다"라고 스케줄러에게 힌트를 주어 동적으로 정책을 바꾸는 지능형 OS가 연구되고 있다.
결론
CPU 바운드와 I/O 바운드는 모든 소프트웨어의 성격을 규정하는 가장 근본적인 두 가지 유전자다. 운영체제의 스케줄링 알고리즘 역사(FCFS $\rightarrow$ RR $\rightarrow$ MLFQ $\rightarrow$ CFS)는 결국 "어떻게 하면 소수의 탐욕스러운 CPU 바운드로부터 다수의 불쌍한 I/O 바운드를 지켜내어 사용자를 답답하지 않게 만들 것인가?"에 대한 투쟁의 역사였다. 소프트웨어 아키텍트가 자신이 만든 코드의 유전자가 어느 쪽인지 모른다면, 그 어떤 화려한 분산 시스템을 구축하더라도 결국 병목의 늪에서 허우적대게 될 것이다.
- 📢 섹션 요약 비유: 물(I/O 바운드)과 기름(CPU 바운드)은 섞일 수 없습니다. 이 둘을 한 컵(서버)에 억지로 저어서 섞으려 하지 말고, 물은 넓은 수로(비동기 I/O)로 흐르게 하고 기름은 화로(고성능 코어)에서 타오르게 분리하는 것이 가장 위대한 시스템 연금술입니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| Context Switch (문맥 교환) | I/O 바운드 프로세스는 쉴 새 없이 자고 깨기를 반복하므로, 필연적으로 CPU 바운드보다 문맥 교환 오버헤드가 극도로 높게 나타남 |
| Asynchronous I/O (비동기 입출력) | I/O 바운드 작업 시 스레드가 잠들며(Block) 낭비되는 메모리/스위칭 오버헤드를 막기 위해, 스레드 1개가 수만 개의 I/O를 던져놓고 딴일 하는 기법 |
| MLFQ (다단계 피드백 큐) | OS 스케줄러가 짧게 일하고 빠지는 I/O 바운드를 최상위 큐(응답 극대화)에 두고, 끝까지 CPU를 쥐고 있는 CPU 바운드를 최하위 큐(처리량 극대화)로 강등시키는 천재적 알고리즘 |
| Scale-Up vs Scale-Out | 병목 해소 전략. CPU 바운드는 칩 하나를 강력하게 만드는 Scale-up이 유리하고, I/O 바운드는 서버 100대를 늘리는 Scale-out이 유리함 |
| Thundering Herd Problem | I/O 바운드 스레드 1만 개를 띄워 놨는데, 디스크 I/O가 끝나는 순간 1만 개가 동시에 깨어나며 CPU를 잡으려 싸우다 시스템이 터지는 현상 |
👶 어린이를 위한 3줄 비유 설명
- 세상에는 두 종류의 프로그램이 있어요. 방에 틀어박혀 하루 종일 10시간 동안 수학 문제만 푸는 '공부벌레(CPU 바운드)'와, 1분 일하고 10분 동안 택배 오기를 기다리며 쉬는 '택배 요정(I/O 바운드)'이에요.
- 운영체제 선생님이 보기에, 택배 요정은 금방 일하고 비켜주니까 택배가 왔을 때 바로 1등으로 일하게 해줘요(응답 속도 최고).
- 반면 공부벌레는 한 번 자리를 잡으면 안 비켜주니까, 다른 애들이 못 놀잖아요? 그래서 선생님은 공부벌레를 구석 자리로 보내고 한 번에 1시간씩 넉넉히 풀게 하되, 택배 요정이 오면 무조건 자리를 양보하게 만든답니다!