TIL-089 | Python_Multiprocessing

Lee, ChankyuΒ·2022λ…„ 3μ›” 2일
post-thumbnail

🌈 Python_Multiprocessing & Multithreading

🧐 μ§€λ‚œ TIL-080 글을 톡해 파이썬의 GIL(Global Interpreter Lock)에 λŒ€ν•΄ ν•™μŠ΅ν–ˆμ—ˆλ‹€. μ²˜λ¦¬ν•΄μ•Όν•˜λŠ” 연산에 따라 Multiprocessing ν˜Ήμ€ Multithreading이 효과적으둜 μž‘μš©ν•  수 μžˆλŠ”λ° κ°„λ‹¨ν•œ 예제λ₯Ό 톡해 νŒŒμ΄μ¬μ—μ„œμ˜ μ μš©λ°©λ²•μ„ μ•Œμ•„λ³΄κ³ μž ν•œλ‹€.

GIL(Global Interpreter Lock)

GIL 정리 포슀트

  • νŒŒμ΄μ¬μ—μ„œλŠ” ν•˜λ‚˜μ˜ ν”„λ‘œμ„ΈμŠ€ μ•ˆμ— λͺ¨λ“  μžμ›μ˜ 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

  1. https://wikidocs.net/85603
  2. https://niceman.tistory.com/145
  3. https://data-make.tistory.com/682
profile
Backend Developer - "Growth itself contains the germ of happiness"

0개의 λŒ“κΈ€