이 레슨과 관련된 학습 키워드
컴퓨터 과학 & 프로그래밍 — 문제 해결의 도구 → Python 프로그래밍 — 첫 코드에서 실전까지 → Python 프로그래밍 — 첫 코드에서 실전까지 → 중급
GIL, threading, multiprocessing, asyncio, concurrent.futures, 데드락, 레이스컨디션, aiohttp, 성능 벤치마크
오늘은 동시성이 왜 필요한지부터 시작해 보겠습니다.
동시성이란 여러 작업이 논리적으로 겹쳐서 진행되는 것입니다.
병렬성은 물리적으로 같은 순간에 실행되는 것을 말하고요.
동시성이 병렬성을 포함하는 더 넓은 개념입니다.
그림 상단을 보시면 순차 vs 동시 실행의 시간 비교가 나와 있어요.
웹 서버가 만 개 요청을 순차 처리하면 1,000초가 걸립니다.
그림 하단 왼쪽을 보시면 이 웹 서버 병목 예시가 있습니다.
동시성이 없으면 CPU 자원 대부분이 유휴 상태로 낭비됩니다.
핵심은 I/O 바운드와 CPU 바운드를 구분하는 것입니다.
I/O 바운드는 외부 자원 응답 대기가 병목입니다.
그림 왼쪽 박스를 보시면 HTTP API 호출, 파일 읽기, DB 쿼리가 예시로 나옵니다.
CPU는 놀고 있으니 대기 시간에 다른 작업을 끼워 넣으면 됩니다.
이때 적합한 전략은 asyncio나 threading입니다.
반면 CPU 바운드는 연산 자체가 병목입니다.
이미지 처리, ML 학습, 암호화 같은 작업이 여기에 해당합니다.
오른쪽 박스를 보시면 스레드를 늘려도 GIL 때문에 이득이 없다고 나옵니다.
CPU 바운드에는 multiprocessing으로 GIL을 우회해야 합니다.
수치로 직접 확인해 볼게요.
10개 API 호출, 각 200ms라면 순차 실행은 2,000ms입니다.
asyncio로 동시 실행하면 약 200ms, 무려 10배 향상됩니다.
8개 이미지 리사이즈, 각 500ms면 순차는 4,000ms입니다.
4코어 멀티프로세싱으로 약 1,000ms, 4배 향상됩니다.
그림 하단 비교표를 보시면 세 가지 모델이 정리되어 있습니다.
threading은 같은 프로세스 내 여러 스레드를 쓰는 방식입니다.
GIL로 CPU 병렬성은 제한되지만 I/O 대기 중 스레드 전환이 가능합니다.
asyncio는 단일 스레드 이벤트 루프로 수천 연결을 처리합니다.
multiprocessing은 별도 프로세스로 진정한 병렬 실행을 합니다.
코어 수에 비례해 빨라지지만 메모리 격리로 IPC가 필요합니다.
식당 주방 비유로 마무리해 드릴게요.
I/O 바운드는 오븐 대기 중 다른 주문을 처리하는 것과 같습니다.
CPU 바운드는 요리사가 직접 칼질하는 시간, 코어를 더 투입해야 합니다.
오븐 앞에 멍하니 서 있는 게 바로 순차 실행의 비효율입니다.