Computer_Language/Python

[Python] 파이썬 병렬 프로그래밍 모듈 (Multiprocessing 라이브러리)

Joo-Topia 2019. 9. 1. 10:57

졸업작품에 Multiprocessing 라이브러리를 사용할 부분이 생겨서 다시 공부할 겸 정리를 해보려고 한다.

Multiprocessing 라이브러리를 공부하기 전 프로세스와 스레드의 개념을 정리하고 공부를 진행하겠다.

 

프로세스 vs 스레드


운영체제 공부를 하면서 프로세스의 정의는 "실행 중인 프로그램"이라고 했던 기억이 난다. 각 프로세스는 프로세스의 상태를 기록하는 프로세스 제어 블록(PCB - Process Control Block)이 존재하는데, 프로세스프로세서에 할당될 때마다 해당 프로세스의 제어 블록들이 먼저 적재된다. 이 과정을 문맥 교환(Context Switch)라고 한다.

스레드의 정의는 "프로그램 내에서 실행되는 흐름의 단위"이다. 한 개의 프로그램을 여러 개의 스레드로 정의할 수 있고, 이러한 방식이 멀티스레드이다. 프로세스의 직접 실행 정보를 제외한 나머지 프로세스 관리 정보, 자원과 메모리를 공유하기 때문에 시스템 성능이 향상될 수 있다.

 

쉬운 말로 비유해보자

"공부"를 프로그램에 비유하면

"손으로 하는 필기"와 "눈으로 보고 습득"은 두 개의 스레드가 되고

공부를 시작하면 프로그램 "공부"를 "공부 프로세스"라고 부를 수 있다.

우리의 뇌를 프로세서에 비유할 수 있고, 멀티 프로세서 환경이라고 가정하면

한 개의 프로세서에는 "손으로 하는 필기"를 다른 프로세서에는 "눈으로 보고 습득"을 실행하여 멀티 스레드가 가능하다.

 

파이썬의 병렬 프로그래밍


파이썬에서 멀티스레드를 지원해주는 라이브러리가 대표적으로 두 종류 있다. Threading 모듈과 multiprocessing 모듈이다. Threading 모듈은 파이썬의 GIL(Global Interpreter Lock)이라는 잠금장치가 있어서 I/O 작업이 아닌 CPU 작업이 많을 경우 오히려 성능이 떨어지게 된다. Lock을 풀고 스레드를 교환하고 다시 Lock을 거는 멀티스레드 방식이 적용되기 때문이다. 파이썬에서도 Thread 보다 multiprocessing 사용을 권장하고 있기 때문에 이 모듈에 대한 공부는 다음으로 미루고 multiprocessing 모듈에 대해 공부를 할 것이다.

 

파이썬의 multiprocessing 모듈은 스레드 아닌 프로세스를 만들어주는 라이브러리라고 생각하면 편할 것 같다.

예제 코드를 통해 확인해보자.

# -*- coding: utf-8 -*-
import time
import os
import multiprocessing
semaphore = 0
def func(x):
    global semaphore
    while semaphore:
        pass
    semaphore = 1
    print("프로세스 id : ", os.getpid())
    print("process_list : ",x)
    semaphore = 0
    time.sleep(2)
    return

if __name__ == '__main__' :
    start = time.time()
    process_list = ['p1','p2','p3','p4']

    for _ in process_list:
        func(_)
    end = time.time()

    print('sequence programming : %s sec' %(end - start))

    start = time.time()

    p = multiprocessing.Pool(processes=4)
    p.map(func,process_list)
    p.close()
    p.join()

    print('parallel programming : %s sec' %(time.time() - start))

결과

소요 시간을 비교해보면 거의 1/4으로 단축된 결과를 볼 수 있다. 유심히 봐야 하는 점은 sequence programming에서 프로세스 id가 하나이고 multiprocess programming(parallel programming)에서는 프로세스 아이디가 4개 인 것이다.

semaphore 변수는 운영체제의 잠금장치 알고리즘을 구현한 플래그이다. (그냥 출력을 예쁘게 하고 싶어서 추가했다.)

다음 네 줄의 코드는 병렬 프로그래밍을 구현하는 코드이다.

p = multiprocessing.Pool(processes=4) - Pool 객체를 생성하여 프로세스를 4개로 지정해준다.
p.map(func, process_list) - Pool에 함수(func)와 인자(process_list)를 맵핑해준다.
p.close() - 자원 낭비를 사전에 막기 위해 close를 호출한다.
p.join() - 작업 완료를 대기한 후 소요 시간을 출력하기 위해 join 함수를 호출한다.

 

보통 웹크롤링으로 병렬 프로그래밍의 성능을 비교하기 때문에 나도 한번 구현해 보았다.

# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
import requests
import multiprocessing
import time

header = {
    'Referer': 'http://prod.danawa.com',
    'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}


def craw_func(x):
    response =  requests.post(url = x,headers = header)
    soup = BeautifulSoup(response.text, 'html.parser')
    price = soup.select('.lowest_price .lwst_prc .prc_c')[0].get_text()
    name = soup.select('.top_summary .prod_tit')[0].get_text()
    print(name, ' : ',price,'원 입니다.')
urls = [
        "http://prod.danawa.com/info/?pcode=5937666&cate=1131326",
        "http://prod.danawa.com/info/?pcode=5941995&cate=1131326",
        "http://prod.danawa.com/info/?pcode=6460148&cate=1131326",
        "http://prod.danawa.com/info/?pcode=6460155&cate=1131326",
        "http://prod.danawa.com/info/?pcode=6168338&cate=1131326",
        "http://prod.danawa.com/info/?pcode=6168351&cate=1131326",
        "http://prod.danawa.com/info/?pcode=5680228&cate=1131326",
        "http://prod.danawa.com/info/?pcode=5680241&cate=1131326",
        ]
if __name__ == '__main__':
    start = time.time()
    p = multiprocessing.Pool(processes=4)
    answer = p.map(craw_func,urls)
    p.close()
    p.join()
    print('%s초'%(time.time() - start))
    '''
    test this
    'processes = 4' vs 'processes = 1'
    '''

결과

processes 옵션을 1과 4로 변화를 주면서 비교해 본 결과이다.

매우 성공적!

 

 


Multiprocessing 라이브러리의 Process와 Queue객체를 이용한 병렬 프로그래밍 방법도 존재하는데 이는 다음번에 사용할 일이 있으면 따로 정리해서 공부해보려고 한다.

오늘 공부는 여기까지!

소스코드는 여기를 참고!