TIL-089 | Python_Multiprocessing
🌈 Python_Multiprocessing & Multithreading
🧐 지난 TIL-080 글을 통해 파이썬의 GIL(Global Interpreter Lock)에 대해 학습했었다. 처리해야하는 연산에 따라 Multiprocessing 혹은 Multithreading이 효과적으로 작용할 수 있는데 간단한 예제를 통해 파이썬에서의 적용방법을 알아보고자 한다.
GIL(Global Interpreter Lock)
- 파이썬에서는 하나의 프로세스 안에 모든 자원의 Lock을 Global하게 관리함으로써 한번에 하나의 쓰레드만 자원을 컨트롤하여 동작
- 여러 쓰레드를 동시에 실행시키지만, 결과적으로는 GIL 때문에 한번에 하나의 쓰레드만 계산을 실행한다.
- GIL로 Garbage collection을 쉽게 구현하게 되었지만, 멀티 코어 부분에서는 아쉬움이 있다.
- 파이썬의 경우에는 GIL이 존재하여 멀티 쓰레드보다는 멀티 프로세스를 사용하는 것이 좋음
Multiprocessing
- 쓰레딩 모듈로 쓰레드를 생성 할 수 있는 것과 동일한 방식으로 프로세스를 생성
- 프로세스는 각자가 고유한 메모리 영역을 가지기 때문에 쓰레드에 비하면 메모리 사용이 늘어난다는 단점
- 싱글 머신 아키텍처로부터 여러 머신을 사용하는 분산 애플리케이션으로 쉽게 전환 가능
파이썬의 Multiprocessing
- 파이썬에서 멀티프로세싱을 이용하여 여러 작업을 동시에 처리할 수 있다.
#Multiprocessing 적용X
import time
start = time.time()
def count(group):
for i in range(1, 50001):
print(f'{group} : {i}')
num_list = ['n1', 'n2', 'n3', 'n4']
for num in num_list:
count(num)
print(f'{time.time()-start} s')
- 멀티프로세싱을 적용하지 않고 20만 카운트를 한 것이다.
Process
- multiprocessing의 Process를 사용하여 Multiprocessing을 간단히 구현할 수 있다.
#multiprocessing - Process 함수 사용
start = time.time()
def count(group):
for i in range(1, 50001):
print(f'{group} : {i}')
if __name__ == '__main__':
process1 = multiprocessing.Process(target=count, args=('pr1',))
process2 = multiprocessing.Process(target=count, args=('pr2',))
process3 = multiprocessing.Process(target=count, args=('pr3',))
process4 = multiprocessing.Process(target=count, args=('pr4',))
process1.start()
process2.start()
process3.start()
process4.start()
process1.join()
process2.join()
process3.join()
process4.join()
print(f'{time.time()-start} s')
process1 = multiprocessing.Process(target=count, args=('pr1',))
코드에서args=('pr1',))
로 작성한 이유는 처음에args=('pr1')
로 작성하였을 때 아래와 같은 에러가 났기 때문이다.
- argument로 어떤 것들이 들어가게 된건지 확인하기 위해 count 함수를 아래와 같이 변경하여 출력해 보았다.
def count(*args):
for i in range(1, 50001):
print(f'{args} : {i}')
- 'pr1' 이 하나의 문자열로 인식되지 않고 iterable하게 인식되는 것을 확인할 수 있다. 그래서
arg=('pr1',)
로 수정하였따.
Pool
- Python에선 multiprocessing.Pool을 이용하여 멀티프로세싱을 할 수 있다.
- Pool은 지정된 개수만큼 프로세스를 미리 만들어 놓고, 그 프로세스들 위에서 작업을 돌리는 방식이다.
- 처음 Pool을 생성할 때에 사용될 프로세스 수를 지정하지 않는다면,
os.cpu_count()
의 값으로 지정된다. - 사용 후 처리로는
close()
와terminate()
이 있다.close()
는 더 이상 Pool에 추가 작업이 들어가지 않는다는 것을 알려주며, 지금 수행 중인 작업이 모두 끝나면 Pool의 프로세스들을 종료한다.terminate()
를 사용하면, 현재 진행 중인 작업이 있더라도 즉시 Pool의 프로세스들을 종료한다.join()
은 Pool의 모든 프로세스들의 종료가 완료되기를 기다린다. - 아래의 간단한 예제 소스코드를 보도록 하겠다.
#multiprocessing
import time
import multiprocessing
start = time.time()
def count(group):
for i in range(1, 50001):
print(f'{group} : {i}')
num_list = ['n1', 'n2', 'n3', 'n4']
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=4)
pool.map(count, num_list)
pool.close()
pool.join()
print(f'{time.time()-start} s')
processes=4
로 설정하여 멀티프로세싱으로 함수를 동작시켰다.- 위의 예시에 비해 시간이 줄어듬을 확인하였다.
- 처음에는 숫자의 범위를 1~10000으로 했었는데 이때는 시간 차이가 크지 않아 범위를 증가시켰더니 속도차이를 확연히 느낄 수 있었다.
🙆♂️ multiprocessing 모듈의 일부 함수만을 사용하여 간단한 멀티프로세싱을 작동해보았다. 상황에 따라 다양한 형태로 응용이 가능하지만 추후 점차 사용 범위를 넓혀 갈 예정이다.
📝 Reference
Author And Source
이 문제에 관하여(TIL-089 | Python_Multiprocessing), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@lck0827/TIL-089-PythonMultiprocessing저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)