python

파이썬 기반의 멀티프로세싱

kimjy 2021. 12. 15. 16:33

이전 포스트(https://kimjy-par.tistory.com/12)에서는 일반적으로 통용되는 멀티스레딩과 멀티프로세싱의 특징을 비교하였다. 본인은 C나 Fortran 언어를 사용하여 병렬처리를 수행한 경험이 있으며, 이 때에는 openMP와 MPI 라이브러리를 사용하였었다. 따라서 처음 파이썬 기반 병렬처리를 공부할 때는 threading은 openMP에, multiprocessing은 MPI 라이브러리에 대응되는 개념이라고 접근하여 코드를 작성하였지만 크나큰 오산이었다. 이전에 멀티스레딩에 대해 포스팅하였던 것 처럼, 파이썬에서 스레딩 모듈을 사용하여 멀티스레딩을 사용한다고 하더라도 하나의 인터프리터는 하나의 프로세서 자원밖에 사용하지 못한다! 따라서 compute-intensive한 프로그램에서는 아무리 스레드를 증가시킨다고 하더라도 성능이 좋아지지 못한다. 다만 I/O 작업이 많거나 네트워크 통신이 많은 프로그램에서는 어느 정도 효과를 보일 수 있을 것이라 생각된다.

 

따라서 compute-intensive한 프로그램이거나, 혹은 일반적인 프로그램의 경우에는 multiprocessing 패키지를 사용하는 것이 좋을 것이라 생각한다. multiprocessing 패키지는 멀티프로세서 자원을 효율적으로 사용할 수 있을 것이라고 기대되며, 클러스터 환경 등과 같이 네트워크로 연결된 컴퓨팅 자원도 사용할 수 있을 것이라고 생각된다. 현재 포스팅 중인 글은 랩탑 환경에서 테스트를 하였고, 실제로 여러 프로세스가 포크되는 것을 확인하였다. 다만, 현재는 클러스터 자원에 접근이 불가능함으로, 추후 클러스터 환경에서 multiprocessing 패키지를 사용하고 이를 분석하는 것도 좋은 공부일 것 같다.

 

기본 사용법

기본적으로 다중 프로세스를 포크할 때는 pool클래스나 process클래스를 사용할 수 있다. 기본적으로 두 클래스 모두 포크할 프로세스의 개수를 정해주는 것은 동일하나, 몇개의 차이점이 존재한다(출처: 링크). 차이점을 논하기에 앞서 먼저 pool과 process 클래스를 사용한 코드를 보는 것이 좋을 것 같다.

 

Pool 클래스를 사용한 병렬화

from multiprocessing import Pool
from time import sleep

#define function for parallelization
def print_function(*args):
    print(args[0])
    sleep(0.1)
    
#make list for input of print_function
input_list=[]
for i in range(5):
    input_list.append( "test:" + str(i))
    
#make process list
processes=[]
for i in range(3):
    process.append(
        Process(target=print_function, args=(input_list[i],))
    )
    
#processes are started
for i in range(3):
    processes[i].start()
    
#processes are ended
for i in range(3):
    processes[i].join
    
>>> test: 0
>>> test: 1
>>> test: 2

 

Process를 사용한 병렬화

from multiprocessing import Pool

def print_function(*args):
    print(args[0])
    sleep(0.1)
    

input_list=[]
for i in range(5):
    input_list.append(
        "test:"+str(i)
    )
    
!parallelization with Pool class
p=Pool(3)
p.map(f, input_list)

>>> test: 0
>>> test: 1
>>> test: 2
>>> test: 3
>>> test: 4

 

아마 두 클래스 모두 기본적인 사용법은 크게 어렵지 않을 것이라고 생각한다. 또 두 방법 모두 병렬화가 가능하나, 아래와 같은 차이가 존재한다.

 

pool 클래스

  • 프로세스가 실행될 함수를 정의하는 것은 동일하나, 따로 데이터를 분배하지 않아도 된다. 예제에서는 리스트의 개수는 5개인데, 3개의 프로세스가 포크되었다. 따라서 3개의 프로세스에 5개의 데이터가 자동으로 매핑되어 실행된다.  
  • process 방법에 비해 pool 방법의 오버헤드가 더 크다. 그리고, 따로 join을 선언하지 않아도 된다.
  • 실행중인 프로세스의 메모리만 할당되어 실행된다.
  • I/O 등에 의해 CPU가 idle되어있을 때, Pool은 context switch가 발생하지 않고 idle 상태가 끝날 때 까지 대기한다.

 

Process 클래스

  • 예제와 같이 데이터를 각각 분배하여야 한다. 또 process의 start 지점과 join 지점을 설정하여야 한다. 
  • 모든 프로세스의 메모리가 할당된다.
  • I/O 등에 의해 CPU가 idle되어 있을 때, 다른 프로세스로 context switch를 수행한다.

 

프로세스 간의 통신 방법

계속 작성 중....