문제

글로벌 인터프리터 잠금이란 무엇이며 왜 문제가 됩니까?

Python에서 GIL을 제거하는 것과 관련하여 많은 소음이 발생했으며 이것이 왜 그렇게 중요한지 이해하고 싶습니다.나는 컴파일러나 인터프리터를 직접 작성한 적이 없으므로 세부 사항에 대해 검소하게 생각하지 마십시오. 이해하려면 아마도 필요할 것입니다.

도움이 되었습니까?

해결책

Python의 GIL은 다른 스레드의 통역사 내부에 대한 액세스를 직렬화하기위한 것입니다. 멀티 코어 시스템에서는 여러 스레드가 여러 코어를 효과적으로 사용할 수 없음을 의미합니다. (길이이 문제로 이어지지 않았다면 대부분의 사람들은 길에 신경 쓰지 않을 것입니다. 멀티 코어 시스템의 유병률이 증가함에 따라 문제로 제기되고 있습니다.) 자세히 이해하려면 자세히 이해하려면, 당신은 볼 수 있습니다 이 비디오 또는 봐 이 슬라이드 세트. 정보가 너무 많을 수도 있지만 자세한 내용을 요청했습니다 :-)

Python의 길은 참조 구현 인 Cpython에게는 실제로 문제 일뿐입니다. Jython과 Ironpython에는 길이 없습니다. 파이썬 개발자로서, 당신은 일반적으로 C 확장을 작성하지 않는 한 길을 가로 질러 오지 않습니다. C 확장자 작가는 확장이 I/O를 차단할 때 GIL을 해제해야하므로 Python 프로세스의 다른 스레드가 실행할 수 있습니다.

다른 팁

그렇지 않은 여러 스레드가 있다고 가정합니다 진짜 서로의 데이터를 만지십시오. 그것들은 가능한 한 독립적으로 실행해야합니다. 함수를 호출하기 위해 획득 해야하는 "글로벌 잠금"이있는 경우 병목 현상이 될 수 있습니다. 처음에 여러 스레드를 사용하면 많은 혜택을 얻지 못할 수 있습니다.

현실 세계의 비유에 넣으려면 : 100 명의 개발자가 단 하나의 커피 머그잔 만있는 회사에서 일하는 것을 상상해보십시오. 대부분의 개발자는 코딩 대신 커피를 기다리는 데 시간을 할애 할 것입니다.

이 중 어느 것도 파이썬에 관한 것이 아닙니다. 나는 파이썬이 처음에 길을 필요로하는 것에 대한 세부 사항을 모른다. 그러나, 그것은 당신에게 일반적인 개념에 대한 더 나은 아이디어를 제공하기를 바랍니다.

먼저 Python Gil이 제공하는 내용을 이해해 봅시다.

모든 작업/명령은 통역사에서 실행됩니다. 길은 통역사가 단일 스레드에 의해 보관되도록합니다. 특정 시간의 순간. 여러 스레드가있는 파이썬 프로그램은 단일 통역사에서 작동합니다. 특정 순간 에이 통역사는 단일 스레드에 의해 유지됩니다. 통역사를 보유하고있는 스레드 만 달리기 ~에 모든 시간.

이제 왜 그게 문제가 되는가 :

기계에는 여러 코어/프로세서가있을 수 있습니다. 여러 코어를 사용하면 여러 스레드가 실행됩니다 동시에 즉, 여러 스레드가 실행될 수 있습니다 특정 시간에.. 그러나 통역사는 단일 스레드에 의해 보관되므로 다른 스레드는 코어에 액세스 할 수 있지만 아무것도하지 않습니다. 따라서 여러 코어가 제공하는 이점을 얻지 못합니다. 어떤 순간에도 단일 코어 만 있는데,이 코어는 현재 통역사를 보유하고있는 스레드에서 사용하는 핵심입니다. 따라서 프로그램은 마치 단일 스레드 프로그램 인 것처럼 실행하는 데 시간이 오래 걸립니다.

그러나 I/O, 이미지 처리 및 Numpy Number Crunching과 같은 잠재적으로 차단되거나 장기 작업이 GIL 외부에서 발생합니다. 가져 왔습니다 여기. 따라서 이러한 작업의 경우 멀티 스레드 작업이 GIL의 존재에도 불구하고 단일 스레드 작업보다 더 빠릅니다. 따라서 Gil은 항상 병목 현상이 아닙니다.

편집 : Gil은 Cpython의 구현 세부 사항입니다. Ironpython과 Jython에는 Gil이 없으므로 진정으로 멀티 스레드 프로그램이 가능해야하며 Pypy와 Jython을 사용한 적이 없다고 생각했습니다.

파이썬은 가장 진정한 단어의 의미에서 멀티 스레딩을 허용하지 않습니다. 멀티 스레딩 패키지가 있지만 코드를 속도를 높이기 위해 멀티 스레드를 원한다면 일반적으로 사용하는 것이 좋지 않습니다. Python에는 GIL (Global Interpreter Lock)이라는 구조물이 있습니다.

https://www.youtube.com/watch?v=ph374fjqfpe

길은 '스레드'중 하나만 한 번에 실행할 수 있는지 확인합니다. 스레드는 길을 획득하고 약간의 작업을 수행 한 다음 길을 다음 스레드로 전달합니다. 이것은 매우 빨리 발생하므로 인간의 눈에 스레드가 병렬로 실행되는 것처럼 보이지만 실제로 동일한 CPU 코어를 사용하여 차례대로 진행되고 있습니다. 이 모든 길로 통과하면 실행에 오버 헤드가 추가됩니다. 즉, 코드를 더 빨리 실행하려면 스레딩 패키지를 사용하는 것이 종종 좋은 생각이 아님을 의미합니다.

Python의 스레딩 패키지를 사용해야 할 이유가 있습니다. 몇 가지 일을 동시에 실행하고 싶다면 효율성이 문제가되지 않으면 완전히 괜찮고 편리합니다. 또는 일부 IO와 같은 것을 기다려야하는 코드를 실행중인 경우 많은 의미가 있습니다. 그러나 스레딩 라이브러리를 사용하면 추가 CPU 코어를 사용할 수 없습니다.

멀티 스레딩은 운영 체제 (멀티 프로세싱을 통해), Python 코드 (예 : Spark 또는 Hadoop)를 호출하는 외부 응용 프로그램 또는 Python 코드가 호출되는 일부 코드 (예 : Python을 가질 수 있습니다. 코드는 값 비싼 멀티 스레드 작업을 수행하는 C 함수를 호출합니다).

두 스레드가 동일한 변수에 액세스 할 때마다 문제가 있습니다. 예를 들어 C ++에서 문제를 피하는 방법은 뮤 테스 잠금 장치를 정의하여 두 개의 스레드가 동시에 객체 세터를 입력하지 않도록하는 것입니다.

파이썬에서는 멀티 스레딩이 가능하지만 하나의 파이썬 명령보다 세분성으로 두 개의 스레드를 동시에 실행할 수 없습니다. 런닝 스레드는 Gil이라는 글로벌 잠금 장치를 얻고 있습니다.

즉, 멀티 코어 프로세서를 활용하기 위해 멀티 스레드 코드를 작성하기 시작하면 성능이 향상되지 않습니다. 일반적인 해결 방법은 멀티 프로세스로 구성됩니다.

예를 들어 C에 쓴 메소드 내부에있는 경우 GIL을 해제 할 수 있습니다.

Gil의 사용은 Python에 내재 된 것이 아니라 가장 일반적인 Cpython을 포함하여 일부 통역사에게 내재되어 있습니다. (#Edited, 댓글 참조)

GIL 문제는 여전히 Python 3000에서 유효합니다.

Python 3.7 문서

나는 또한 다음의 인용문을 강조하고 싶습니다. 파이썬 threading 선적 서류 비치:

CPython 구현 세부 사항:CPython에서는 전역 인터프리터 잠금(Global Interpreter Lock)으로 인해 한 번에 하나의 스레드만 Python 코드를 실행할 수 있습니다(특정 성능 지향 라이브러리가 이 제한을 극복할 수 있음에도 불구하고).애플리케이션이 멀티 코어 머신의 계산 리소스를 더 잘 활용하도록 하려면 다음을 사용하는 것이 좋습니다. multiprocessing 또는 concurrent.futures.ProcessPoolExecutor.그러나 여러 I/O 바인딩 작업을 동시에 실행하려는 경우 스레딩은 여전히 ​​적합한 모델입니다.

이는 다음으로 연결됩니다. 다음에 대한 용어집 항목 global interpreter lock 이는 GIL이 Python의 스레드 병렬 처리가 적합하지 않음을 암시한다는 것을 설명합니다. CPU 바인딩된 작업:

한 번에 하나의 스레드만 Python 바이트코드를 실행하도록 보장하기 위해 CPython 인터프리터에서 사용하는 메커니즘입니다.이는 객체 모델(dict와 같은 중요한 내장 유형 포함)을 동시 액세스에 대해 암시적으로 안전하게 만들어 CPython 구현을 단순화합니다.전체 인터프리터를 잠그면 멀티프로세서 시스템이 제공하는 병렬 처리의 상당 부분을 희생하면서 인터프리터가 멀티스레드되는 것이 더 쉬워집니다.

그러나 표준 또는 타사의 일부 확장 모듈은 압축이나 해싱과 같이 계산 집약적인 작업을 수행할 때 GIL을 해제하도록 설계되었습니다.또한 I/O를 수행할 때 GIL은 항상 해제됩니다.

"자유 스레드" 인터프리터(공유 데이터를 훨씬 더 세밀하게 잠그는 인터프리터)를 만들려는 과거의 노력은 일반적인 단일 프로세서 사례에서 성능이 저하되었기 때문에 성공하지 못했습니다.이 성능 문제를 극복하면 구현이 훨씬 더 복잡해지고 유지 관리 비용이 더 많이 들 것으로 생각됩니다.

이 인용문은 또한 dicts와 변수 할당이 CPython 구현 세부 사항으로서 스레드로부터 안전하다는 것을 의미합니다:

다음으로, 에 대한 문서 multiprocessing 패키지 프로세스를 생성하면서 GIL을 극복하는 동시에 인터페이스와 유사한 인터페이스를 노출하는 방법을 설명합니다. threading:

multiprocessing은 스레딩 모듈과 유사한 API를 사용하여 프로세스 생성을 지원하는 패키지입니다.다중 처리 패키지는 로컬 및 원격 동시성을 모두 제공하여 스레드 대신 하위 프로세스를 사용하여 전역 해석기 잠금을 효과적으로 회피합니다.이로 인해 멀티프로세싱 모듈을 통해 프로그래머는 특정 시스템에서 여러 프로세서를 완전히 활용할 수 있습니다.Unix와 Windows 모두에서 실행됩니다.

그리고 에 대한 문서 concurrent.futures.ProcessPoolExecutor 사용한다고 설명한다 multiprocessing 백엔드로:

ProcessPoolExecutor 클래스는 프로세스 풀을 사용하여 호출을 비동기적으로 실행하는 Executor 하위 클래스입니다.ProcessPoolExecutor는 다중 처리 모듈을 사용하여 전역 해석기 잠금을 피할 수 있지만 피클 가능한 객체만 실행하고 반환할 수 있음을 의미합니다.

이는 다른 기본 클래스와 대조되어야 합니다. ThreadPoolExecutor 저것 프로세스 대신 스레드를 사용

ThreadPoolExecutor는 호출을 비동기적으로 실행하기 위해 스레드 풀을 사용하는 Executor 하위 클래스입니다.

그로부터 우리는 다음과 같은 결론을 내렸습니다. ThreadPoolExecutor I/O 바인딩 작업에만 적합하지만 ProcessPoolExecutor CPU 바인딩된 작업도 처리할 수 있습니다.

다음 질문은 애초에 GIL이 존재하는 이유를 묻습니다. 글로벌 통역사 잠금이 필요한 이유는 무엇입니까?

프로세스 대 스레드 실험

~에 멀티프로세싱과 스레딩 Python 저는 Python에서 프로세스와 스레드를 실험적으로 분석했습니다.

결과의 빠른 미리보기:

enter image description here

Python (Cpython 및 기타)이 Gil을 사용하는 이유

에서 http://wiki.python.org/moin/globalinterpretretlock

CPYTHON에서 글로벌 통역사 잠금 또는 길은 여러 기본 스레드가 한 번에 파이썬 바이트 코드를 실행하는 것을 방지하는 뮤트입니다. 이 잠금 장치는 주로 Cpython의 메모리 관리가 스레드 안전하지 않기 때문에 필요합니다.

파이썬에서 제거하는 방법?

Lua와 마찬가지로 Python은 여러 VM을 시작할 수 있지만 Python은 그렇게하지 않으므로 다른 이유가 있다고 생각합니다.

Numpy 또는 일부 Python 확장 라이브러리에서는 때로는 GIL을 다른 스레드로 공개하면 전체 프로그램의 효율성이 향상 될 수 있습니다.

시각 효과에 대한 책 멀티 스레딩의 예를 공유하고 싶습니다. 여기에 고전적인 데드 잠금 상황이 있습니다

static void MyCallback(const Context &context){
Auto<Lock> lock(GetMyMutexFromContext(context));
...
EvalMyPythonString(str); //A function that takes the GIL
...    
}

이제 시퀀스의 이벤트를 고려하여 데드 로크가 발생합니다.

╔═══╦════════════════════════════════════════╦══════════════════════════════════════╗
║   ║ Main Thread                            ║ Other Thread                         ║
╠═══╬════════════════════════════════════════╬══════════════════════════════════════╣
║ 1 ║ Python Command acquires GIL            ║ Work started                         ║
║ 2 ║ Computation requested                  ║ MyCallback runs and acquires MyMutex ║
║ 3 ║                                        ║ MyCallback now waits for GIL         ║
║ 4 ║ MyCallback runs and waits for MyMutex  ║ waiting for GIL                      ║
╚═══╩════════════════════════════════════════╩══════════════════════════════════════╝
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top