소개

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
"""
  1. do_thing 함수
    • 2개의 argument 를 받는 함수입니다.
    • 시간이 걸리는 I/O 작업을 sleep 으로 표현하였어요
  2. for 문을 이용한 process 생성 및 실행
    • 사용법은 threading 과 거의 흡사합니다.
    • target 을 통해서 process 로 실행할 함수를 지정하고, args 를 통해서 배열로 함수의 인자를 넘겨줍니다.
  3. process.join()
    • process 가 종료할때까 기다려 줍니다.
    • 없다면, 마지막 줄의 perf time 까지 실행되어 버립니다.
    • threading 에서와 마찬가지 입니다.
  4. 출력
    • 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

참고

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Back To Top