소개
Python 에서 병렬처리를 위한 threading 과 Multiprocessing 중에서 이번에는 Multiprocessing 에 대해서 정리해보고자 합니다. 앞선 포스팅을 통해서 각각의 특징을 정리해보았었는데요.
다시 한번 간단히 정리해보자면, Multiprocessing 은 CPU bound 작업에서 보다 더 유리한 면을 가지고 있습니다. CPU bound 작업이라고 하면 I/O bound (Networking, File upload, download )작업과 대조되는 작업으로, CPU 사용이 더욱 많은 작업을 말합니다. 예를 들자면 이미징 관련 작업, 복잡한 수학적 계산 작업등을 생각하면 될 것같아요.
그렇다면 python 에서 이 Multiprocessing 을 이용한 병렬 처리는 어떻게 구현이 될까요?
이것 역시 Threading 과 마찬가지로 이전의 multiprocessing 모듈을 이용하는 방식과, python 3.2 이후 버전에서 생긴 concurrent.futures 을 이용하는 방법이 있습니다.
Multiprocessing (Old 방식)
multiprocessing 모듈을 이용한 old 한 방식으로 먼저 구현을 해보았습니다.
다소 시간이 걸리는 CPU bound 작업을 do_thing 함수에 구현하였습니다. 아래 예제에서는 sleep 을 시간이 걸리는 I/O 작업이라고 가정하였어요.
import multiprocessing
import time
# 소요 시간 확인 위한 시작 시간
start_time = time.perf_counter()
# multiprocessing 으로 실행 시킬 함수
def do_thing(secs, name):
seconds = secs
print(f'{seconds} seconds sleeping {name}')
time.sleep(seconds)
print(f'done, {name}')
if __name__ == '__main__':
""" main """
processes = []
for _ in range(5):
p = multiprocessing.Process(target=do_thing, args=[2, 'jang'])
p.start() # process 시작
processes.append(p)
for process in processes:
process.join() # process 작업 끝날 때까지 기다리기
end_time = time.perf_counter()
print(f'perf time: {round(end_time - start_time, 2)}')
""" 출력
2 seconds sleeping jang
2 seconds sleeping jang
2 seconds sleeping jang
2 seconds sleeping jang
2 seconds sleeping jang
done, jang
done, jang
done, jang
done, jang
done, jang
perf time: 2.16
"""
- do_thing 함수
- 2개의 argument 를 받는 함수입니다.
- 시간이 걸리는 I/O 작업을 sleep 으로 표현하였어요
- for 문을 이용한 process 생성 및 실행
- 사용법은 threading 과 거의 흡사합니다.
- target 을 통해서 process 로 실행할 함수를 지정하고, args 를 통해서 배열로 함수의 인자를 넘겨줍니다.
- process.join()
- process 가 종료할때까 기다려 줍니다.
- 없다면, 마지막 줄의 perf time 까지 실행되어 버립니다.
- threading 에서와 마찬가지 입니다.
- 출력
- multiprocessing 을 통해 병렬 처리를 하여, 총 실행시간이 2.16 초 임을 알 수 있어요
Concurrency.futures (New 방식)
python 3.2 버전 이상부터 concurrent.futures 모듈이 나오면서 multiprocess 역시 간편히 이용할 수 있게 되었습니다.
import concurrent.futures
import time
# 소요 시간 확인 위한 시작 시간
start_time = time.perf_counter()
# multiprocessing 으로 실행 시킬 함수
def do_thing(secs, name):
seconds = secs
print(f'{seconds} seconds sleeping {name}')
time.sleep(seconds)
print(f'done, {name}')
if __name__ == '__main__':
""" main """
# max_workers 인수가 없을 시에는 실행되고 있는 머신의 기본 프로세스 수를 따름
with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor:
resurts = [executor.submit(do_thing, 2, 'jang') for _ in range(5)]
end_time = time.perf_counter()
print(f'perf time: {round(end_time - start_time, 2)}')
""" 출력
2 seconds sleeping jang
2 seconds sleeping jang
2 seconds sleeping jang
2 seconds sleeping jang
2 seconds sleeping jang
done, jang
done, jang
done, jang
done, jang
done, jang
perf time: 2.26
concurrent.futures 의 ProcessPoolExecutor 는 multiprocess 기반으로 만들어진 더 최신의 모듈로서, 더욱 사용되길 권장하고 있습니다.
More Reading
참고
- https://docs.python.org/ko/3/library/multiprocessing.html
- https://docs.python.org/ko/3/library/multiprocessing.html#multiprocessing-programming