2024년 1학기/AI영상인식실습

AI영상인식실습 6주차 - 파이썬 멀티스레딩과 GIL

서사대생 2024. 4. 19. 07:44

AI영상인식실습 6주차 수업에서, Open3D 구성 파일을 만들 때 python_multi_threading 옵션을 true로 지정하면 멀티스레딩으로 실행한다는 얘기를 들었다. 파이썬에 GIL(Global Interpreter Lock)이 있는데 멀티스레딩이 어떻게 가능한지 궁금해서 알아봤다.

 

현재 CPython 구현체에서는 GIL 때문에 멀티스레딩을 완벽하게 지원하지 않는다. GIL은 한 번에 하나의 스레드만이 Python 코드를 실행할 수 있도록 제한하여, CPU가 여러 코어를 가진 현대의 컴퓨터 환경에서도 진정한 병렬 처리가 어렵게 만든다.

GIL 때문에 발생하는 주요 문제는 멀티스레딩이 필요한 과학 및 수치 계산 작업에서 효율적으로 멀티코어 CPU를 사용할 수 없다는 것이다. 예를 들어, PyTorch와 같은 라이브러리는 데이터 로딩과 같은 작업을 병렬로 처리하기 위해 프로세스 기반의 API를 사용하고 있지만, 이로 인해 사용자에게 더 복잡하고 혼란스러운 경험을 제공할 수 있다.

이 문제를 해결하기 위해 Python 개발자들은 GIL을 선택적으로 만들거나 제거하는 다양한 방안을 모색하고 있으며, 이는 Python의 멀티스레딩 성능을 개선할 수 있는 잠재적인 변화를 의미한다.

 

Itamar Turner-Trauring이 2022년 4월 28일에 처음 작성하고 2022년 10월 25일에 마지막으로 업데이트한 "When Python can’t thread: a deep-dive into the GIL’s impact" 기사는 GIL이 스레딩과 병렬 처리에 미치는 영향을 자세히 다루었다. 주요 내용은 다음과 같다.

  1. GIL의 영향: GIL은 한 번에 하나의 스레드만이 Python 바이트코드를 실행할 수 있게 제한하여, 특히 CPU 바운드 작업에서 멀티 스레딩을 크게 방해한다.
  2. GIL 이해를 위한 정신 모델:
       - 기사에서는 GIL의 작동 방식을 설명하기 위해 점점 더 정확해지는 정신 모델의 진행을 설명했다. 기본적으로 GIL 때문에 한 번에 하나의 스레드만 Python 코드를 실행할 수 있다.
       - 스레드가 기본적으로 5밀리초마다 전환될 수 있지만, 이는 최선의 노력 기반 메커니즘이며 긴 작업을 실행하는 스레드는 보장되지 않는다.
  3. 확장과 GIL: C 확장 사용은 GIL 관련 문제를 복잡하게 하거나 완화할 수 있다. Python의 내부와 상호 작용하지 않고 긴 작업을 수행하는 확장은 GIL을 해제하고 다른 스레드의 병렬 실행을 허용할 수 있다. 그러나 이러한 작업이 Python 객체와 상호 작용해야 하는 경우 GIL을 다시 획득해야 하므로 병목 현상이 발생할 수 있다.
  4. 실제 예시:
       - Python의 sleep 함수를 사용하는 예시를 통해 GIL이 어떻게 동작하는지 보여줬다. sleep 함수는 스레드를 일시 중지하는 동안 GIL을 해제하여 다른 스레드가 병렬로 실행될 수 있다.
       - GIL을 해제하지 않고 무거운 계산을 수행하는 작업은 다른 스레드의 실행을 차단할 수 있다.
  5. GIL 제한 완화 전략: 멀티 프로세싱(여러 프로세스를 실행하여 각 프로세스가 자체 GIL을 가지게 하는 방법) 사용을 포함하여 GIL의 영향을 최소화하는 방법을 제안했다. 또한 GIL을 적절히 관리하는 효율적인 C 확장을 작성하는 방법을 제안했다.

Bartosz Zaczyński가 2023년 9월 13일에 작성한 기사 "Bypassing the GIL for Parallel Processing in Python"는 Python에서 GIL의 제한을 극복하는 다양한 방법을 심층적으로 탐구한다. 주요 내용은 다음과 같다.

  1. 병렬 처리의 기초 회고: 병렬 처리, 동시 수행과 순차 실행의 비교, 현대 컴퓨터가 병렬 처리를 선호하는 이유에 대해 설명한다.
  2. 멀티스레딩과 GIL: Python의 GIL이 CPU 바운드 작업의 진정한 병렬 실행을 어떻게 방해하는지, 그리고 Java 스레드가 어떻게 두 종류의 작업을 모두 효율적으로 처리할 수 있는지에 대해 설명한다.
  3. 프로세스 기반 병렬 처리: Python에서 진정한 병렬 처리를 달성하기 위해 스레드 대신 프로세스를 사용하는 것이 좋다. Python의 `multiprocessing`과 `concurrent.futures`가 프로세스를 효과적으로 관리하는 방법에 대해 논의한다.
  4. 대체 런타임과 GIL 면역 라이브러리: GIL을 포함하지 않는 대체 Python 런타임 환경(Jython 및 IronPython 등)과 Python의 GIL 외부에서 자체 스레드를 관리하는 라이브러리(예: NumPy)를 사용하는 것을 탐색한다.
  5. C 확장 및 Cython 사용: GIL을 해제하는 C 확장을 작성하는 방법과 Cython을 사용하여 C 확장을 보다 쉽게 생성하는 방법에 대해 자세히 설명한다.
  6. 이미지 처리를 위한 실제 적용: 병렬 이미지 처리를 위한 데스크탑 애플리케이션을 구축하는 실제 튜토리얼을 통해 논의된 방법을 실제 시나리오에서 구현하는 방법을 보여준다.

Bartosz Zaczyński이 GIL이 없는 대체 Python 런타임 환경으로 언급한 Jython은 Python 2.7을 기반으로 개발되었기 때문에 최신 Python 기능과 라이브러리를 지원하지 않는 문제가 있어 지금은 널리 사용되지 않는다. 그러고 보니 "Jython 완벽 안내서"를 번역한 지가 14년이나 됐다.

2023년 7월에는 CPython에서 GIL을 선택적으로 사용하게 하는 제안(PEP 703)을 스티어링 위원회에서 승인했다. 장기(5년 이상)적으로 no-GIL 빌드만이 유일한 빌드가 되게 하는 것이 목표다. CPython 핵심 개발자인 Thomas Wouters가 2023년 10월에 게시한 "PEP 703 (Making the Global Interpreter Lock Optional in CPython) acceptance"에서, 이 결정은 GIL이 없는 Python이 이론적으로 많은 이점을 가져다 줄 수 있으며, 대다수 커뮤니티가 이에 동의한다고 보기 때문이라고 밝혔다. GIL은 멀티 코어 CPU에서 애플리케이션의 확장성을 높이고 성능을 향상시킬 수 있는 잠재력을 가지고 있으며, 이를 제거하는 것이 CPython에 긍정적인 영향을 미칠 것으로 예상된다.
그러나 GIL을 제거하면 기존의 확장 모듈이 깨지거나 CPython의 성능 및 유지 관리성이 저하될 수 있다는 우려도 있다. PEP 703의 구현 계획은 몇 단계에 걸쳐 진행된다.

  • 1단계: 실험적 단계로, 빌드 시 옵션을 통해 free-threaded 빌드를 활성화한다. 이 단계에서는 커뮤니티와 타사 패키지가 테스트를 수행할 수 있다.
  • 2단계: API 및 ABI 변경 사항이 안정화되고 커뮤니티의 충분한 지원이 확보되면 지원되지만 기본 설정은 아닌 단계로 넘어간다.
  • 3단계: free-threaded 빌드가 기본값이 되는 단계다. 이 단계에서는 필요한 모든 변경 사항이 완료되고, 커뮤니티와 핵심 개발자들이 이에 대한 준비가 되어 있어야 한다.

free-threaded 빌드로 인한 성능 영향은 10~15%의 성능 저하가 예상되지만, 이는 잠재적인 이점을 고려할 때 충분히 감수할 가치가 있다고 여겨진다. PEP 703의 승인은 신중하게 이루어졌으며, 너무 많은 문제를 일으킬 경우 변경 사항을 되돌릴 수 있는 조치가 포함되어 있다.

 

다시 Open3D로 돌아가서, Open3D는 joblib을 사용해 멀티스레딩을 구현한다고 공식 문서에 나와 있다.

'2024년 1학기 > AI영상인식실습' 카테고리의 다른 글

AI영상인실실습 2주차  (3) 2024.03.19