A thumbnail was created from banner.godori.dev
νμ΄μ¬μ 곡λΆνμ λ λΆλ€μ GIL μ΄λΌλ κ²μ ν λ²μ© λ€ λ€μ΄ 보μ
¨μκ±°λΌ μκ°ν©λλ€
Global Interpreter Lock μ μ½μμΈ μ΄κ²μ
νμ΄μ¬ μΈν°ν리ν°κ° ν μ°λ λλ§ νλμ λ°μ΄νΈμ½λλ§ μ€νν μ μκ² νλ κ²μ΄μ£
GILμ νλμ μ°λ λμκ² λͺ¨λ μμμ μ μ λ₯Ό νλ½ν©λλ€
λ°λλ‘ μκΈ°νλ©΄, λ€λ₯Έ μ°λ λλ μμμ Acquire νκΈ° μ μλ μμ μ€νμ‘°μ°¨ λ μ μλ€λ λ»μ΄μ£
νμ΄μ¬μ λͺ¨λ κ²μ κ°μ²΄μ
λλ€. κ·Έλ¦¬κ³ CPython μμ κ°κ°μ κ°μ²΄λ€μ
νλμ C ꡬ쑰체μ λμλ©λλ€. κ²°κ΅ μμ²λκ² κ°μ²΄κ° λ§μ΄ μ‘΄μ¬νλ€λ κ²μ΄μ£
κ° νμ΄μ¬ κ°μ²΄λ GC λ₯Ό μν΄ reference count (μ°Έμ‘° νμ) λ₯Ό κ°μ§κ³ μμ΅λλ€
reference count λ λ³λ ¬ νλ‘κ·Έλλ°μμ νν μ ν μ μλ
곡μ μμ μ κ·Όμμ rade condition λ¬Έμ λ₯Ό κ²ͺμ μ μμ΅λλ€
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
(μ PyObject
μμ ob_refcnt κ° μ°Έμ‘° νμλ₯Ό μΉ΄μ΄ν
νλ κ° μ
λλ€)
κ·Έλ λ€κ³ , κ° κ°μ²΄κ° ob_refcnt μ μ κ·Όν λ λ§λ€ κ°λ³ mutex λ₯Ό νμ©ν΄μ
thread-safe νλλ‘ λ§λ€κΈ°μλ μ±λ₯μμ μν΄μ, deadlockμ μν λ±μ΄ μ‘΄μ¬ν©λλ€
κ·Έλμ κ·Έλ₯ μ κΆμ΅λλ€. μΈν°ν리ν°λ₯Ό μ κΆμ΅λλ€ π
λ§μ κΈμμ κ·Έ λΉμμ μ»΄ν¨ν
νκ²½μ μκ°νλ©΄, λΉμ°ν μ νμ΄λΌκ³ ν©λλ€
λ³λ ¬ νλ‘κ·Έλλ°μμ μκΈ°νλ μμ κ°μ λκΈ°ν μ λμ λ°λ₯Έ λΆλ₯λ₯Ό ν¬κ²
μΌλ‘ λΆλ₯νλλ°, μ΄λ νλ‘μΈμμ νμ μμ
κ°μ ν΅μ μ μ λλ₯Ό μλ―Έν©λλ€.
μμ
κ°μ λκΈ°νμ ν΅μ μ΄ μ¦μΌλ©΄, fine-grained lock μΌλ‘ λλμλ€κ³ νλ©°
κ·Έλ μ§ μλ€λ©΄, coarse-grained lock μΌλ‘ λλμλ€κ³ ν©λλ€
νμ΄μ¬μ GILμ νλμ μ°λ λκ° μΈν°ν리ν°μ λͺ¨λ κ²μ κ°μ Έκ°λλ€
κ΅μ₯ν ν°ννκ² Lock μ΄ λ°μνκΈ° λλ¬Έμ
μ¬μ€μ coarse-grained λλ embarrassingly parallel(μ²μΉ κ³€λ λ³λ ¬) μ ννλΌ ν μ μμ΅λλ€
GIL λ‘ μΈν΄ λ€λ₯Έ μΈμ΄μ κ°μ΄ λ©ν°μ°λ λ© νλ‘κ·Έλλ°μ΄ μ νλμ΄
λκ° μ±λ₯μμ μμ μ μμκ°λ€κ³ μκ°μ΄ λμκ² μ§λ§
μ€μ λ‘ Single Thread νκ²½μμμ μ±λ₯μ μ’μ΅λλ€
κ·Έλ¦¬κ³ GILμ λν κ°μ μμ λνμ κ·λ λ° λ‘μΈμ μ΄λ κ² μκΈ°νμ΅λλ€
But I also don't expect it to go away
until someone other than me goes through the effort of removing it,
and showing that its removal doesn't slow down single-threaded Python code.
λ¨μΌ μ°λ λ μ±λ₯μ μ νμν€μ§ μμΌλ©΄μ ν λ² κ°μ ν΄μ μλ΄
νμ΄μ¬μ μ°λ λκ° μΈν°ν리ν°λ₯Ό μ μ νλ€κ°, I/O Bound μμ
(λ€νΈμν¬, νμΌI/O λ±) μ΄ λ°μνλ©΄
μΈν°νλ¦¬ν° μ μ λ₯Ό release λ₯Ό νκ² λ©λλ€
λ§μ½μ, HTTP Request λ₯Ό μ°λ λλ₯Ό μ¬μ©ν΄μ μμ
νκ² λλ€λ©΄
μ±λ₯ κ°μ μ΄ μμκ² κ°λ€λ μκ°μ΄ λλλ€
(κ·Έ μμΈν μ½λλ μλμμ...)
CPU Bound Task μ κ²½μ°μλ νμ΄μ¬μμ λ©ν°μ€λ λλ‘ κ΅¬ννλ κ²½μ°μ κ°κ±°λ λ λλ €μ§λλ€
λ λλ €μ§λ μ΄μ λ μ°λ λκ°μ Lock μ Acquire / Release νλ κ³Όμ μμ
Context Switching μ λν μ€λ²ν€λκ° μμΈ μΌκ² κ°μ΅λλ€
CPU Bound Task λ CPU κ° μ²λ¦¬νλ©΄ λ λΉ λ₯Έ μμ
λ€μ μλ―Έν©λλ€... ν€ν€
μ£Όλ‘ μ°μ°μμ
μ΄ λ κ² κ°κ³ , μ¬μ€ I/O Bound Task λ₯Ό μ μΈν κ²μ λͺ¨λ CPU μμ μ²λ¦¬νκ² μ£
I/O Bound Task λ burst λλ μκ°μ λλΆλΆμ I/O μ ν μ νλ μμ
μ μλ―Έν©λλ€
μ΄λ₯Όν
λ©΄, μΉμμ μ΄λ€ λ°μ΄ν°λ₯Ό λ°μμ€κ±°λ, νμΌμ μ½κ³ μ°λ λ±μ μμ
μ CPU 보λ€
νμΌ μμ€ν
κ³Ό λ€νΈμν¬ λ± νμ μ»΄ν¬λνΈκ° κ·Έ μμ
μ λλ΄κΈ°λ₯Ό κΈ°λ€λ¦¬λ μκ°μ΄ κΈΈλ€λ κ²μ΄μ£
μλ₯Ό λ€λ©΄, Network μμ
, Database I/O, Disk I/O λ±μ΄ μμ΅λλ€
μ΄λ° μμ
μ νμ΄μ¬ λ©ν° μ°λ λλ‘ κ΅¬ν νλ€λ©΄ μ‘°κΈ λ μ±λ₯ κ°μ μ΄ μμ κ² κ°μμ
λκΈ°μ μΌλ‘ (μΌλ°μ μΈ ννλ‘) ꡬκΈμ μ°κ΄ κ²μμ΄ 10κ°λ₯Ό λ°μμ€λ μ½λλ₯Ό ν
μ€νΈ νμ΅λλ€
λ€νΈμν¬ νκ²½μ λ°λΌ λ€λ₯΄κ² μ§λ§, λλ΅ 3~4μ΄ μ λκ° μμ λ©λλ€
urls = [
'https://www.google.com/complete/search?q=seungri&cp=1&client=psy-ab&xssi=t&gs_ri=gws-wiz&hl=ko&authuser=0&pq=google&psi=ty6OXKGCIMnFmAXDraP4Bw.1552821944139&ei=ty6OXKGCIMnFmAXDraP4Bw',
# μλ μΆκ° 9κ°μ urlλ€...
]
@timer # timer λ°μ½λ μ΄ν°λ μμ
μ μμλλ μκ°μ νλ¦°νΈ νλ λ°μ½λ μ΄ν° μ
λλ€
def fetch_single_thread(urls):
for url in urls:
response = fetch(url)
print(response)
if __name__ == '__main__':
fetch_single_thread(urls) # Time Elasped 3.02 seconds
μλμ κ°μ΄ λ©ν°μ°λ λ©μΌλ‘ ꡬννλ€λ©΄, μ±λ₯μμΌλ‘ κ°μ μ΄ λ κ²μ΄λΌ μκ°ν©λλ€
κ²°κ³Όκ° μ λ€μ΄μλμ§ νμΈνκΈ° μν΄ Queue λ₯Ό λ§λ€μ΄, κ²°κ³Όλ₯Ό Queue μ λ£μ΅λλ€
μμμκ°μ μ½ 0.33 μ΄ μ
λλ€
@timer
def fetch_four_threads(urls):
threads = []
for url in urls:
thread = threading.Thread(target=fetch, args=(url,))
threads.append(thread)
for t in threads:
t.start()
for t in threads:
t.join()
if __name__ == '__main__':
fetch_four_threads(urls)
"νμ΄μ¬μ GIL μ΄ μμΌλ λ©ν°νλ‘μΈμ€λ§ μ¨μΌν΄" μ²λΌ μμΌνκ² μκ°νμλλ°
μ΄λ² κΈμ κ³κΈ°λ‘, μ΄λ μ λλ λͺ
ννκ² Python μ GIL μ λν΄ μ΄ν΄ν μ μμμ΅λλ€
κΈμ μ°λλ° λ°μ΄λΉ λΉμ¦λ¦¬λΌλ μ λͺ
ν λΆμ μ¬λΌμ΄λλ₯Ό λ§μ΄ μ°Έκ³ νμ΅λλ€
(μ λ μ μ¬λΌμ΄λμ λ΄μ©μ λ€ μ΄ν΄νκ² μλλλ€... 40 νΌμΌνΈλ λͺ» μμ λ€μκ² κ°λ€μ)
CS κΈ°λ³Έ μ§μμ΄ λΆμ‘±νμ¬, μ°λ λμ νλ‘μΈμ€μ κ°λ
μ μ λͺ¨λ₯΄λ
μ¬λ¬κ°μ§ μ’μ κΈμ μ΄ν΄νλλ° μκ°μ΄ λ§μ΄ κ±Έλ Έμ΅λλ€
νΉμ κ΄λ ¨ν μ§μμ μ΅λνλλ° λμμ΄ νμνμλ€λ©΄
μ κ° κ³΅λΆνκ³ μλ μ νλΈ μ¬μλͺ©λ‘ μ΄ λμμ΄ λλ€λ©΄ μ’κ² λ€μ!
Davie Beazley μ 2010 PyCon Slide - Understanding the Python GIL
νμ΄μ¬μ μ€λ λ μ¬μ©λ²
Parallel HTTP Requests in Python
μμ κ°λ° λΈλ‘κ·Έ - Global Interpreter Lockμ΄λ?
μμ΄κ° μ«μ κ°λ°μ - Pythonμμ threadλ₯Ό μ¬μ©νμ§ λ§μΈμ?
pythonμ GIL(Global Interpreter Lock)
Understanding Threads in Python
Real Python - What is the Python Global Interpreter Lock (GIL)?
κ°λ°μλ°λ‘κ·Έ - μ Pythonμλ GILμ΄ μλκ°
νμ΄μ¬ GIL κΉμν! (δΈ)
β 2006λ
μ΄λ©΄ μ λ κ°λ°μ κ°λ λͺ¨λ₯΄λ λ κ°μλ° μ λ°°λλ€μ λ΄κ³΅ μ λ§ λλ¨ νμλλ€...
μ’μ κΈ κ°μ¬ν©λλ€! celery μ threading μ 곡λΆνλ μμ€μ κΆκΈν μ μ΄ λ§μ΄ ν΄κ²°λμλ€μ, μμΌλ‘λ μ°Έκ³ νλ¬ μ’ μ’ λ€λ₯΄κ² μ΅λλ€.