AboutPython

[파이썬] Global Interpreter Lock (GIL)이란?

scone 2024. 4. 13. 10:05

하나의 스레드가 두 개의 작업을 연속적으로 수행한 것에 비해, 두 개 스레드가 각각 하나의 작업을 수행했을 때 더 오래걸렸다고 합니다. 왜 이런 일이 발생했을까요?

import random
import threading
import time


def working():
    max([random.random() for i in range(500000000)])


# 1 Thread
s_time = time.time()
working()
working()
e_time = time.time()
print(f'{e_time - s_time:.5f}')


# 2 Threads
s_time = time.time()
threads = []
for i in range(2):
    threads.append(threading.Thread(target=working))
    threads[-1].start()

for t in threads:
    t.join()

e_time = time.time()
print(f'{e_time - s_time:.5f}')

#single 129.12857
#multi 170.34252

 

파이썬에서 Global Interpreter Lock(GIL)이 무엇인가요?

더보기

파이썬 인터프리터가 한 스레드만 하나의 바이트코드를 실행 시킬 수 있도록 해주는 잠금기능(Lock)이다. 하나의 스레드가 작업을 끝낼때까지 모든 자원을 쓰게 두고 그 후에는 lock을 걸어서 다른 스레드는 실행되지 않도록 하는 것이다. 즉, 병렬 실행이 불가능합니다.

파이썬의 객체들에 대한 접근을 보호하는 일종의 뮤텍스 라고 할 수 있겠습니다.

https://user-web.icecube.wisc.edu/~dschultz/docs/slides/software_bootcamp/gil.html#4

  • 참고로 뮤텍스란?
    • 임계구역(Critical Section)을 가진 쓰레드들의 실행시간(Running Time)이 서로 겹치지 않고 각각 단독으로 실행되도록 하는 기술

 

GIL이 왜 필요한 것인가요?

더보기

참조 횟수에 기반하여 GC를 실행하는 Python의 특성상, Python 인터프리터가 동시에 실행되면 Race Condition이 발생할 수 있습니다. 따라서 여러개의 쓰레드가 동시에 인터프리터를 수행하지 못하도록 잠그는 것입니다.

  • 참고로 Garbage Collection ( GC )란?
    • 파이썬에서 모든 것은 객체이다. 그리고 각 객체는 참조 횟수를 저장하기 위한 필드를 가지고 있는데, 파이썬에서의 GC는 이러한 참조횟수를 확인하여 값이 0이 되면 해당 객체를 메모리에서 삭제시킨다.
    • https://callmescone.tistory.com/399

 

Race Condition이 무엇인가요?

더보기

하나의 값에 여러 쓰레드가 동시에 접근함으로써 값이 올바르지 않게 읽히거나 쓰일 수 있는 상태를 말합니다. 이러한 상황을 보고 Thread-safe하지 않다고 표현하기도 합니다.

  • 참고로 Thread-safe란?
    • 하나의 함수가 한 스레드로부터 호출되어 실행 중일 때, 다른 스레드가 그 함수를 호출하여 동시에 함께 실행되더라도 각 스레드에서의 함수의 수행 결과가 올바로 나오는 것을 말합니다.

 

파이썬에서 멀티 쓰레딩이 그럼 의미 없다는 것일까요?

더보기

I/O 작업을 할 때는, GIL이 관여하지 않으므로 I/O 집약적인 일에는 효율적인 방식이 될 수 있습니다.

 

또는 threading 모듈 대신 multiprocessing 모듈을 사용할 경우, 멀티 프로세서와 별개로 메모리를 사용하여 완전히 독립하여 병렬 프로그래밍을 할 수 있습니다. 단 여러 개의 CPU가 있는 멀티코어 환경에서만 가능합니다.

 

 

  • 코드 예시
더보기

multiprocessing.Process 를 사용한 예시

import time


def heavy_work(name):
    result = 0
    for i in range(4000000):
        result += i
    print('%s done' % name)


if __name__ == '__main__':
    import multiprocessing

    start = time.time()
    procs = []
    for i in range(4):
        p = multiprocessing.Process(target=heavy_work, args=(i, ))
        p.start()
        procs.append(p)

    for p in procs:
        p.join()  # 프로세스가 모두 종료될 때까지 대기

    end = time.time()

    print("수행시간: %f 초" % (end - start))

 

multiprocessing.Pool을 사용한 예시

import time


def heavy_work(name):
    result = 0
    for i in range(4000000):
        result += i
    print('%s done' % name)


if __name__ == '__main__':
    import multiprocessing

    start = time.time()
    pool = multiprocessing.Pool(processes=4)
    pool.map(heavy_work, range(4))
    pool.close()
    pool.join()

    end = time.time()

    print("수행시간: %f 초" % (end - start))

 

multiprocessing.Pool은 포크할 프로세스의 개수를 지정할 수 있다는 특징이 있습니다.

 

 

출처 : https://wikidocs.net/124290

 

Multiprocessing 모듈을 사용하지 않고 맨 처음의 문제를 개선시킬 수 있나요?

더보기

sleep()을 사용하여 개선시킬 수 있습니다!

싱글스레드에서는 sleep으로 인해 아무런 동작도 취하지 못한 체 동작을 대기하는 반면에 멀티스레드에서는 sleep으로 멈춘 경우 다른 스레드로 context switching하여 효율이 개선된다.

 

 

  • 코드
더보기
import random
import threading
import time


def working():
    time.sleep(0.1)
    max([random.random() for i in range(10000000)])
    time.sleep(0.1)
    max([random.random() for i in range(10000000)])
    time.sleep(0.1)
    max([random.random() for i in range(10000000)])
    time.sleep(0.1)
    max([random.random() for i in range(10000000)])
    time.sleep(0.1)
    max([random.random() for i in range(10000000)])
    time.sleep(0.1)


# 1 Thread
s_time = time.time()
working()
working()
e_time = time.time()
print(f'{e_time - s_time:.5f}')


# 2 Threads
s_time = time.time()
threads = []
for i in range(2):
    threads.append(threading.Thread(target=working))
    threads[-1].start()

for t in threads:
    t.join()

e_time = time.time()
print(f'{e_time - s_time:.5f}')

#1 = 11.66885
#2 = 10.76572

 

 

출처

https://velog.io/@sicksong/Python-GILGlobal-Interpreter-Lock