- μ°μ°μ λ³λ ¬λ‘ μ μ©
- κ·Έλ¬λ μ μ μΈν°νλ¦¬ν° λ½(GIL) λ‘ μΈν΄ νμ΄μ μ€λ λλ μ§μ ν λ³λ ¬ μ€ν λΆκ°
- μ±λ₯μ κ°μ₯ κ²°μ μ μΈ μν₯μ λ―ΈμΉλ λΆλΆμ C μΈμ΄λ₯Ό μ¬μ©ν νμ₯ λͺ¨λλ‘ μμ±
- C λ₯Ό μ¬μ©νλ©΄ λ‘μ° λ 벨μ κ°κΉκ² μ μ© κ°λ₯νμ¬ νμ΄μ μ λΉν΄ λΉ λ¦
- νμ΄μ μ GILκ³Όλ μκ΄μμ΄ CPU μ½μ΄ νμ© κ°λ₯
- SWIG(https://github.com/swig/swig), CLIF(https://github.com/google/clif)
- Cλ‘ μ½λ μ¬μμ±νλ κ²μ λ¨μ
- νμ΄μ μμ μ§§κ³ μ΄ν΄νκΈ° μ¬μ΄ μ½λκ° C μμλ μ₯ν©νκ³ λ³΅μ‘ν μ μμ
- ν¬ν μ λ²κ·Έ μ¬λΆ νμΈ νμ
- μ½λμ ν λΆλΆλ§ C λ‘ λ°κΎΈλ©΄ λλ κ²½μ°κ° λλ¬Όλ€λ μ
- κ±°μ λλΆλΆμ μ½λλ₯Ό ν¬ν ν΄μΌ ν¨
- κ·Έλ‘ μΈν΄ ν μ€νΈμ νμμ± μ¦λ, μ½λμ μνμ± μ¦λ
- νμ΄μ μμ Cλ‘ νΈνκ² μ νμμΌμ£Όλ μ€ν μμ€ λꡬ
- CPtyhon (https://cython.org/)
- Numba (https://numba.pydata.org/)
multiprocessingλ΄μ₯ λͺ¨λ μ¬μ©- μμ νλ‘μΈμ€λ₯Ό λ€λ₯Έ νμ΄μ μΈν°ν리ν°λ₯Ό μ€ννμ¬ νμ΄μ μμ μ¬λ¬ CPU μ½μ΄λ₯Ό νμ©ν μ μμ
- μμ νλ‘μΈμ€λ μ£Ό μΈν°ν리ν°μ λ³λλ‘ μ€νλλ―λ‘ GIL λΆλ¦¬λ¨
# mymodule.py
def gcd(pair):
a, b = pair
low = min(a, b)
for i in range(low, 0, -1):
if a % i == 0 and b % i == 0:
return i
assert False, 'λλ¬ν μ μμ# run_serial.py
import my_module
import time
NUMBERS = [
(1963309, 2265973), (2030677, 3814172),
(1551645, 2229620), (2039045, 2020802),
(1823712, 1924928), (2293129, 1020491),
(1281238, 2273782), (3823812, 4237281),
(3812741, 4729139), (1292391, 2123811),
]
def main():
start = time.time()
results = list(map(my_module.gcd, NUMBERS))
end = time.time()
delta = end - start
print(f'μ΄ {delta:.3f} μ΄ κ±Έλ¦Ό')
if __name__ == '__main__':
main()
>>>
μ΄ 0.911 μ΄ κ±Έλ¦Ό- μ μμ λ λ μμ μ΅λ곡μ½μλ₯Ό ꡬνλ ꡬν
- μμ°¨ μ€ν μ μ°μ° μκ° μ νμ μΌλ‘ μ¦κ°
- GILλ‘ μΈν΄ νμ΄μ¬μ΄ μ¬λ¬ μ€λ λλ₯Ό λ€μ€ CPU μ½μ΄μμ λ³λ ¬ μ€νν μ μμΌλ―λ‘ μ€λ λλ₯Ό νμ©ν΄λ μλ ν₯μ κ±°μ μμ
# run_threads.py
import my_module
from concurrent.futures import ThreadPoolExecutor
import time
NUMBERS = [
...
]
def main():
start = time.time()
pool = ThreadPoolExecutor(max_workers=2)
results = list(pool.map(my_module.gcd, NUMBERS))
end = time.time()
delta = end - start
print(f'μ΄ {delta:.3f} μ΄ κ±Έλ¦Ό')
if __name__ == '__main__':
main()
>>>
μ΄ 1.436 μ΄ κ±Έλ¦Ό- μ μμ λ
concurrent.futuresλͺ¨λμ μλThreadPoolExecutorν΄λμ€λ₯Ό νμ©νμ¬ λ κ°μ μμ μ μ€λ λλ‘ μν - μ€λ λ ν μμ, νκ³Ό ν΅μ νλ λ‘μ§μΌλ‘ μΈν΄ μ€ν μκ°μ΄ μ’ λ μ¦κ°
# run_parallel.py
import my_module
from concurrent.futures import ProcessPoolExecutor
import time
NUMBERS = [
...
]
def main():
start = time.time()
pool = ProcessPoolExecutor(max_workers=2) # μ΄ λΆλΆλ§ λ°κΏ
results = list(pool.map(my_module.gcd, NUMBERS))
end = time.time()
delta = end - start
print(f'μ΄ {delta:.3f} μ΄ κ±Έλ¦Ό')
if __name__ == '__main__':
main()
>>>
μ΄ 0.683 μ΄ κ±Έλ¦Ό-
concurrent.futuresμ μλProcessPoolExecutorμ¬μ© -
ProcessPoolExecutorμμ- [parent] μ΄ κ°μ²΄λ μ
λ ₯ λ°μ΄ν°λ‘ λ€μ΄μ¨
mapλ©μλμ μ λ¬λNUMBERSμ κ° μμλ₯Ό μ·¨ν¨ - [parent] μ΄ κ°μ²΄λ μ λ¬λμ΄ μ»μ μμλ₯Ό
pickleλͺ¨λμ μ¬μ©νμ¬ μ΄μ§ λ°μ΄ν°λ‘ μ§λ ¬ν - [parent, child] μ΄ κ°μ²΄λ λ‘컬 μμΌμ ν΅ν΄ μ£Ό μΈν°νλ¦¬ν° νλ‘μΈμ€λ‘λΆν° μμ μΈν°νλ¦¬ν° νλ‘μΈμ€μκ² μ§λ ¬νν λ°μ΄ν°λ₯Ό 볡μ¬
- [child] μ΄ κ°μ²΄λ λ€μ μμ§λ ¬ν
- [child] μ΄ κ°μ²΄λ
gcdν¨μκ° λ€μ΄ μλ λͺ¨λ import - [child] μ΄ κ°μ²΄λ μ
λ ₯ λ°μ΄ν°μ λν΄
gcdν¨μλ₯Ό μ€ν, λ€λ₯Έ μμ μΈν°νλ¦¬ν° νλ‘μΈμ€μ λ³λ ¬λ‘ μ€ν - [child] μ΄ κ°μ²΄λ
gcdν¨μμ κ²°κ³Όλ₯Ό μ΄μ§ λ°μ΄ν°λ‘ μ§λ ¬ν - [parent, child] μ΄ κ°μ²΄λ λ‘컬 μμΌμ ν΅ν΄ μμ μΈν°νλ¦¬ν° νλ‘μΈμ€λ‘λΆν° λΆλͺ¨ μΈν°νλ¦¬ν° νλ‘μΈμ€μκ² μ§λ ¬νν κ²°κ³Ό λ°μ΄ν° μ λ¬
- [parent] μ΄ κ°μ²΄λ λ€μ μμ§λ ¬ν
- [parent] μ¬λ¬ μμ νλ‘μΈμ€κ° λ°νν κ²°κ³Όλ₯Ό λ³ν©νμ¬ list λ‘ μμ±
- [parent] μ΄ κ°μ²΄λ μ
λ ₯ λ°μ΄ν°λ‘ λ€μ΄μ¨
-
λΆλͺ¨μ μμ νλ‘μΈμ€ μ¬μ΄ λ°μ΄ν°λ₯Ό μ£Όκ³ λ°μ λ λ§λ€ μ§λ ¬ν λ° μμ§λ ¬ν λ°μ, μΆκ°λΉμ©μ΄ νΌ
-
μ΄ λ°©μμ μ½λ κ° μμ‘΄μ±μ΄ μκ³ λ λ²λ¦¬μ§κ° ν° μ νμ μμ μ μ ν©
- λ λ²λ¦¬μ§ : μ£Όκ³ λ°μμΌ νλ λ°μ΄ν°μ ν¬κΈ°λ μμΌλ μμ νλ‘μΈμ€κ° μ°μ°ν΄μΌ ν μμ μλΉν ν° κ²
- μ²μμλ
multiprocessingλͺ¨λμ μ¬μ©νμ§ μκ³ μμ°¨μ μΌλ‘ μ½λλ₯Ό μμ± ThreadPoolExecutorλ₯Ό ν΅ν΄ νμΈ- κ·Έλλ μ±λ₯ λ¬Έμ κ° μμΌλ©΄
ProcessPoolExecutorλ₯Ό μ¬μ©ν κ²μ κΆμ₯