torch.nn.DataParallel(DP) 과 torch.nn.DataDistributedParallel(DDP)의 차이점을 살펴보던 중
GIL 관련 이야기가 나와서 작성해봅니다. 😄
Python에서는 multi-processing을 써라?
아래 글은 pytorch 공식 문서에서 발췌한 글입니다.
It is recommended to use DistributedDataParallel, instead of DataParallel to do multi-GPU training, even if there is only a single node.
The difference between DistributedDataParallel and DataParallel is: DistributedDataParallel uses multiprocessing where a process is created for each GPU, while DataParallel uses multithreading. By using multiprocessing, each GPU has its dedicated process, this avoids the performance overhead caused by GIL of Python interpreter.
해석해 보자면 DP 대신 DDP를 사용하는 것을 권장하고,
그 이유로 DDP는 multi-processing 을 사용하기 때문에 multi-threading을 사용하는 DP에 비해 GIL로 인해 발생하는 오버헤드를 줄일 수 있다는 점을 들고 있습니다.
위 문장을 제대로 이해하기 위해서는 멀티 프로세싱, 멀티 스레딩, 파이썬의 메모리관리법 및 GIL에 대해 알고 있어야합니다.
Multi-processing? Multi-threading?
멀티 프로세싱이란 말 그대로 여러 개의 프로세서가 하나 이상의 작업을 동시에(병렬적으로) 처리하는 것을 의미합니다.
프로세스 간에는 메모리를 독립적으로 사용하고, 프로세스 중 하나에 문제가 생기더라도 작업이 정지되는 문제는 발생하지 않기 때문에 안전성이 높습니다.
멀티 스레딩은 멀티 프로세싱과 달리 메모리를 공유한다는 특징이 있습니다.
메모리와 프로세스에 대해 더 자세히 알고 싶다면 해당 포스터를 참고해주세요 :)
Python의 multi-threading
기본적으로 파이썬 코드는 여러개의 스레드에 의해 동시에 실행될 수 없습니다.
이론상 하드웨어가 멀티코어 기기라면 여러 개의 스레드가 여러 코어에서 동시에 병렬 동작할 수 있어야 하는데,
파이썬에서는 이와 같은 병렬 동작이 불가능합니다.
이는 파이썬의 GIL이 동시에 코드가 실행되는 것을 막기 때문입니다.
정확히 말하자면 파이썬 인터프리터로 가장 많이 사용되는 CPython에 GIL이 동시에 코드가 실행되는 것을 막습니다.
CPython이 아닌 인터프리터를 사용하면, GIL 문제는 발생하지 않을 수도 있습니다 ㅎ,,
Python의 GIL
GIL이란 Global Interpreter Lock의 약자로 한번에 하나의 스레드만이 코드를 실행 시킬 수 있도록 해주는 일종의 뮤텍스(mutex) 입니다.
하나의 스레드에 모든 자원(메모리)을 허락하고 Lock을 걸어 다른 스레드는 접근할 수 없게 막아버리는 것이죠.
Python의 메모리 관리 - GIL을 사용하는 이유
그렇다면 파이썬은 왜 이런 비효율적인 GIL을 사용할까요?
이는 바로 파이썬(CPython)의 메모리 관리방식 때문입니다.
파이썬에서 모든 것은 단순한 값이 아닌 객체(object)입니다.
그리고 파이썬에서는 Garbage Collection(GC)를 위해 각 객체의 참조 횟수(reference count)를 저장하고 있습니다.
이 참조 횟수가 0이 되면 파이썬 인터프리터는 해당 객체를 메모리에서 삭제합니다.
파이썬에서 sys.getrefcount(object) 함수를 통해 실제로 object의 참조 횟수를 출력해본 결과는 아래와 같습니다.
코드에서 참조하는 횟수보다 1씩 더 크게 나오는 이유는 sys.getrefcount() 함수도 해당 object를 참조하고 있는 것으로 보기 때문입니다.
만약 멀티스레드에서 이 참조 횟수를 동시에 변경하려고 하면, GC가 제대로 이루어지지 않을 수 있습니다.
따라서 파이썬에서는 GIL을 통해 여러 스레드가 같은 객체의 참조횟수를 동시에 변경하는 것을 방지합니다.
이 GIL 때문에 파이썬에서는 멀티스레딩을 하더라도 실제로는 한 번에 하나의 스레드만 실행됩니다.
게다가 스레드 간에는 context switching 비용이 발생하기 때문에 오히려 멀티스레딩이 싱글 스레드 작업보다 오래 걸리는 경우도 발생합니다.
따라서 CPU 연산이 주를 차지하는 프로그램의 경우 파이썬에서 멀티스레딩을 사용하면 오히려 작업시간이 오래 걸릴 수 있습니다.
그렇다면 파이썬에서는 멀티 스레딩을 쓰지 않는 것이 무조건 더 좋을까요?
Python에서 multi-threading을 사용하면 좋은 경우
물론 파이썬에서도 멀티스레딩을 사용했을 때 더 좋은 경우가 있습니다.
바로 I/O작업을 많이 수행해야하는 경우와 sleep 등의 연산으로 스레드가 일정 시간을 대기해야하는 경우 입니다.
한 스레드가 일정 시간을 대기하는 동안 다른 스레드가 일을 처리하면 파이썬에서도 멀티스레딩을 사용했을 때 일을 병렬로 처리할 수 있습니다.
하지만 I/O작업이 많지 않거나, sleep등으로 인한 대기가 필요하지 않은 경우에는 멀티 스레딩 보다 프로세싱의 성능이 더 좋을 수 있습니다.
이와 같은 이유로 처음 소개한 파이토치 문서에서는 DP보다 DDP사용을 권장하고 있는 것 같네요 :)
파이썬 GIL에 대해 더 자세히 알아보고 싶으신 분들은 다음 포스터를 읽어보시면 좋을 것 같습니다!
잘못된 내용이 있다면 알려주시길 바랍니다! 감사합니다 😊
'개발 > 💻 컴퓨터 공학' 카테고리의 다른 글
[Python] re 모듈로 문자열 처리하기 (7) | 2024.11.13 |
---|---|
[Python] try 구문 모든 예외 처리하기 (4) | 2024.11.07 |
[Python] Python은 난수를 어떻게 생성할까? (4) | 2024.10.29 |
[OS] Process(프로세스)란? (1) | 2024.09.02 |