요약:
- Python의
subprocess모듈과psutil라이브러리는 지난 15년 동안 프로세스 종료 대기(wait()) 시sleep과waitpid를 반복하는 비효율적인 'Busy-loop 폴링' 방식을 사용해왔음. - 이 방식은 불필요한 CPU Wake-up, 배터리 소모, 프로세스 종료 감지 지연(Latency) 문제를 야기하며, 다수의 프로세스를 모니터링할 때 확장성(Scalability)이 떨어짐.
- 최근 업데이트를 통해 Linux에서는
pidfd_open()과poll(), BSD/macOS에서는kqueue()를 활용한 진정한 '이벤트 기반 대기(Event-driven waiting)'가 구현됨. - Windows는 이미
WaitForSingleObject를 사용하고 있어 변경 사항이 없으나, POSIX 시스템에서는 불필요한 컨텍스트 스위칭이 제거되고 CPU 사용량이 '0'에 수렴하게 됨.
상세요약:
1. 15년 동안 지속된 문제: Busy-loop 폴링
Python 3.3에서 subprocess.Popen.wait()에 timeout 파라미터가 추가된 이래로, Python 표준 라이브러리와 널리 쓰이는 psutil 라이브러리는 프로세스 종료를 기다리기 위해 비효율적인 방식을 사용해왔습니다.
기존 로직은 다음과 같이 단순하지만 비효율적이었습니다:
-
waitpid(WNOHANG)으로 프로세스 상태 확인 (Non-blocking) - 종료되지 않았으면 잠시
sleep()(Exponential backoff 적용) - 1번으로 돌아가 반복
# 기존 방식 (개념 코드)
import time, os
def wait_busy(pid, timeout):
delay = 0.0001
while True:
# 프로세스 종료 여부 확인 (polling)
if os.waitpid(pid, os.WNOHANG) == (pid, status):
return status
time.sleep(delay)
delay = min(delay * 2, 0.040) # 최대 40ms까지 대기 시간 증가
이 방식은 다음과 같은 3가지 치명적인 단점이 있습니다.
- CPU Wake-ups: 아무리 대기 시간을 늘려도 시스템은 주기적으로 깨어나 상태를 확인해야 하므로 CPU 사이클을 낭비하고 전력을 소모합니다.
-
Latency (지연): 프로세스가 실제로 종료된 시점과
sleep에서 깨어나 이를 감지하는 시점 사이에 필연적인 시간 차이가 발생합니다. - Scalability (확장성): 수백, 수천 개의 프로세스를 동시에 모니터링해야 하는 서버 환경에서는 이러한 오버헤드가 급격히 증가합니다.
2. 해결책: POSIX 시스템을 위한 이벤트 기반 대기 (Event-driven Waiting)
모든 POSIX 시스템은 파일 디스크립터(File Descriptor)의 상태 변화를 감지하는 메커니즘(select, poll, epoll, kqueue)을 제공합니다. 최근 Python과 psutil은 이를 프로세스 PID 감지에 활용하는 방식으로 개선되었습니다.
-
Linux: 2019년 Linux 5.3 커널에 도입된
pidfd_open()시스템 콜을 활용합니다. 이는 프로세스 PID를 가리키는 파일 디스크립터를 반환하며, 이를poll()이나epoll()에 등록하여 프로세스 종료 이벤트를 감시할 수 있습니다. (Python 3.9부터os모듈에 추가됨) -
BSD / macOS:
kqueue()시스템 콜의EVFILT_PROC필터를 사용하여 프로세스 이벤트를 효율적으로 모니터링합니다. -
Windows: 이미
WaitForSingleObjectAPI를 통해 이벤트 기반 대기를 지원하고 있었으므로 변경 사항이 없습니다.
3. 성능 개선 및 결과
이 변화를 통해 wait() 호출 시 프로세스는 커널 입장에서 'Interruptible sleep' 상태가 됩니다. 즉, CPU를 전혀 소모하지 않고 커널 공간에서 조용히 대기하다가, 프로세스 종료 시그널이 발생하면 즉시 깨어납니다.
/usr/bin/time -v 등을 통해 벤치마킹한 결과, 기존 방식 대비 불필요한 컨텍스트 스위칭(Context Switching)이 획기적으로 감소했으며, 프로세스 종료 감지 속도 또한 즉각적으로 개선되었습니다. 이 업데이트는 psutil 라이브러리와 CPython 코어에 반영되어, 향후 Python 개발자들은 별도의 코드 수정 없이 성능 향상 혜택을 누릴 수 있게 되었습니다.