All about

멀티 프로세싱을 활용하면 여러 작업을 별도의 프로세스를 생성 후 병렬처리해서 더 빠르게 결과를 얻을 수 있다.

멀티 프로세싱을 잘 활용하면 멀티코어의 CPU 장점을 잘 살릴 수 있지만, 병렬 프로그래밍의 이해 없이 코드를 작성하면 싱글 프로세스보다 더 느린 경우나, 예상하지 못한 결과가 나올 수 있으니 프로세스, 쓰레드에 대한 이해가 필요하다.

 

* 멀티 프로세싱은 메모리 사용률이 높아지는 단점이 있다.

 

 

Process 생성

import os
from multiprocessing import Process

num = 42

def f(name):
    global num
    num += 1
    print('pid of parent:', os.getppid())
    print('pid of %s : %d' %(name, os.getpid()))
    print('%d' %num)
 
if __name__ == '__main__':
    print('pid of main:', os.getpid())
 
    p1 = Process(target=f, args=("proc_1",))
    p2 = Process(target=f, args=("proc_2",))
 
    p1.start(); p1.join()
    p2.start(); p2.join()

 

실행해보면 프로세스 id 가 각각 다른 것을 확인할 수 있다. .start()는 프로세스 시작, .join()은 프로세스 종료, 좀비 프로세스가 생성되는 것을 막기 위해 가급적이면 .join() 을 사용해서 프로세스를 종료시키는 것이 좋다.

 

pid of parent: 1
pid of __main__ : 237
43
pid of parent: 237
pid of proc_1 : 238
44
pid of parent: 237
pid of proc_2 : 239
44

 

위 코드를 실행해보면 부모 프로세스가 num 값을 43 으로 만든 것을 자식 프로세스가 각각 상속받아 공유하지 않는 것을 확인할 수 있다. 프로세스간 데이터를 주고 받는 것(혹은 공유 메모리 사용)은 Queue, Pipe, Value, Array 등을 통해 할 수 있다.

 

Pool

import time
from multiprocessing import Pool

def count(process_name):
	for i in range(1, 100001):
    	print(process_name, i)

if __name__=="__main__":
	start_time = time.time()
    p_list = ["proc_1", "proc_2", "proc_3", "proc_4"]
    pool = Pool(processes = 4)
    pool.map(count, p_list)
    pool.close()
    pool.join()
    
    print(time.time() - start_time)

 

11번 라인의 processes 값을 바꿔가며 전체 실행시간을 비교해보면 processes 가 높을 수록 실행시간이 줄어드는 경향을 확인할 수 있다.

 

Pool 과 Process 의 차이

링크 참조
https://www.ellicium.com/python-multiprocessing-pool-process

 

Queue

from multiprocessing import Process, Queue

def f(q, l:list):
    q.put(l)

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=f, args=(q,[42, None, 'process 1']))
    p2 = Process(target=f, args=(q,[31, None, 'process 2']))
    p1.start()
    print(q.get())    # prints "[42, None, 'process 1']"
    p1.join()

    p2.start()
    print(q.get())    # prints "[31, None, 'process 2']"
    p2.join()

 

각 프로세스는 Queue를 공유할 수 있다.

 

Pipe

from multiprocessing import Process, Pipe

def f(conn):
    print(conn.recv()) # prints "[31, None, 'send from parent_conn']"
    conn.send([42, None, 'send from child_conn'])
    conn.close()

if __name__ == '__main__':
    parent_conn, child_conn = Pipe()
    parent_conn.send([31, None, 'send from parent_conn'])
    p = Process(target=f, args=(child_conn,))
    p.start()
    print(parent_conn.recv())   # prints "[42, None, 'send from child_conn']"
    p.join()

 

Pipe 를 사용하면 프로세스를 pair 로 생성해서 서로 정보를 주고 받을 수 있다.

라인 10에서 보낸 데이터를 함수 f 를 통해 라인 4에서 받는 것을 볼 수 있다. 

 

Value, Array

from multiprocessing import Process, Value, Array

def f(n, a, num):
    n.value = num
    for i in range(len(a)):
        a[i] = -a[i]

if __name__ == '__main__':
    num = Value('d', 0.0)
    arr = Array('i', range(10))

    p1 = Process(target=f, args=(num, arr, 1))
    p1.start()
    p1.join()

    p2 = Process(target=f, args=(num, arr, 2))
    p2.start()
    p2.join()

    print(num.value)
    print(arr[:])

 

Value, Array 객체를 통해 프로세스간 공유 메모리를 참조하는 것을 확인할 수 있다.

 

참고:

https://docs.python.org/ko/3/library/multiprocessing.html

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading