2024.08.28 TIL
요즘 파이썬으로 DL 모델의 inference 서버를 만들고 있다. main.py
에서 다른 스크립트를 subprocess
로 실행시키도록 코드를 짰었는데, 문득 multiprocessing
과 subprocess
가 어떤 차이인지, 나의 경우 무엇을 사용하는 것이 적합한지 알아보고 변경해야겠다는 생각이 들었다.
multiprocessing
과 subprocess
는 모두 Python에서 병렬 처리를 다룰 때 사용하는 모듈이다. 즉, 프로세스를 생성하고 독립적으로 실행될 수 있도록 지원하는 것이다. 두 모듈 모두 새로운 프로세스를 생성하며, 그 말은 곧 부모 프로세스와 별도의 메모리 공간을 가진다는 것이다.
- 사용 목적
- multiprocessing
- Python 코드 내부에서 병렬 처리를 수행할 때 사용한다.
- 여러 Python 함수 및 메서드를 병렬로 실행하거나, 데이터 병렬성을 처리할 때 주로 사용된다.
- CPU의 집약적인 작업이나 GIL(Global Interpreter Lock)을 피하고자 할 때 유용하다.
- GIL(Global Interpreter Lock)이란?
- Python 인터프리터에서 하나의 스레드만이 한 번에 Python 바이트코드를 실행할 수 있도록 하는 메커니즘이다. 즉, 멀티스레딩 환경에서도 사실상 하나의 스레드만이 Python 코드를 실행하게 만든다는 것을 의미한다. (다음에 GIL에 대해서도 공부를 하고 글을 써보도록 하겠다…)
- GIL(Global Interpreter Lock)이란?
- subprocess
- 외부 프로그램, 명령어 또는 다른 스크립트를 실행할 때 사용한다.
- Python 외부의 작업을 처리하고, 결과를 Python으로 가져와야 할 때 사용한다.
- 운영체제의 명령어나 외부의 독립적인 프로그램을 실행할 때 사용한다.
multiprocessing
은 Python 스크립트를 프로세스로 실행하고 싶을 때 사용하면 되고,subprocess
는 Python으로 쓰이지 않은 다른 프로그램이나 외부 프로그램 혹은 운영체제 명령어 등을 이용하고 싶을 때 사용하면 된다.
- multiprocessing
- 프로세스 간 통신
- multiprocessing
multiprocessing.Queue
,Pipe
,Manager
과 같은 데이터 구조를 이용할 수 있다.- Python 프로세스 간에 객체나 메모리 상의 데이터 구조를 공유할 수 있다.
- subprocess
- 프로세스 간의 통신은 표준 입출력(standard input/output) 스트림을 통해 이루어진다.
subprocess.Popen
을 사용하여 표준 입출력, 오류 스트림을 캡쳐할 수 있다.- Python과 외부 프로그램 간의 간단한 데이터를 주고받기에 적합하다.
- multiprocessing
- 데이터 공유
- multiprocessing
- 부모 프로세스와 자식 프로세스 간에 데이터를 안전하게 공유할 수 있는 메커니즘을 제공한다.
Value
와Array
같은 공유 메모리 구조를 사용하여 데이터 공유가 가능하다.
- subprocess
- 데이터 공유를 위한 특별한 메커니즘이 없다.
- multiprocessing
- OS 의존성
- multiprocessing
- 플랫폼에 따라 프로세스 생성 방식이 다를 수 있다. Unix 계열에서는
fork
를 사용하고 Windows에서는spawn
을 사용한다.
- 플랫폼에 따라 프로세스 생성 방식이 다를 수 있다. Unix 계열에서는
- subprocess
- 모든 주요 운영체제에서 사용할 수 있으며 운영체제의 기본 쉘을 통해 명령어를 실행한다.
- multiprocessing
요약하면, multiprocessing
은 Python 내부에서 병렬 작업을 위한 도구로, 프로세스 간에 데이터를 공유하거나 병렬 작업을 수행할 때 유용하다. 반면, subprocess
는 외부 프로그램이나 명령어를 실행하고, 이와 관련된 입출력 처리를 할 때 적합하다.
그렇다면 둘 다 사용 가능한 Python 스크립트를 실행하는 시나리오에서는 어떤 것을 선택해야 할까?
작업이 Python 내부에서 수행되고, GIL 우회나 복잡한 데이터 공유가 필요하다면 multiprocessing
, 작업이 외부 프로그램에 위임될 수 있고, Python은 단순히 이를 실행하고 결과를 받아오면 된다면 subprocess
를 사용하면 된다.
나의 경우에는 main.py
에서 script A, B, C, D를 실행해야 하는데 4개 모두 Python 스크립트이며, 그 중 A와 B에서는 서로 프로세스 간 통신이 이루어져야 하므로 multiprocessing
을 택했다.
아래는 이전 코드와 바뀐 코드이다. (일부 변수는 간단하게 바꿨다.)
(이전 코드)
import multiprocessing
import subprocess
def run_script(script_name, queue=None):
if queue:
subprocess.run(["python", script_name, queue])
else:
subprocess.run(["python", script_name])
if __name__ == "__main__":
# multiprocessing.Queue 생성
shared_queue = multiprocessing.Queue()
# 스크립트 이름 리스트
scripts = [
"app/A.py",
"app/B.py",
"app/C.py",
"app/D.py"
]
# 각 스크립트를 subprocess로 실행
processes = []
for script in scripts:
if script in ["app/A.py", "app/B.py"]:
# queue를 사용하는 스크립트
p = multiprocessing.Process(target=run_script, args=(script, shared_queue))
else:
# queue를 사용하지 않는 스크립트
p = multiprocessing.Process(target=run_script, args=(script,))
p.start()
processes.append(p)
# 모든 프로세스가 종료될 때까지 대기
for p in processes:
p.join()
(바뀐 코드)
import multiprocessing
from A import A
from B import B
from C import C
from D import D
if __name__ == "__main__":
# 큐 생성
shared_queue = multiprocessing.Queue()
# 프로세스 생성
processes = [
multiprocessing.Process(target=A, args=(shared_queue,)),
multiprocessing.Process(target=B, args=(shared_queue,)),
multiprocessing.Process(target=C),
multiprocessing.Process(target=D),
]
# 프로세스 시작
for process in processes:
process.start()
# 프로세스 종료 대기
for process in processes:
process.join()