programing

동시성:C/C++로 작성된 파이썬 확장자는 글로벌 인터프리터 잠금의 영향을 받습니까?

easyjava 2023. 9. 11. 22:27
반응형

동시성:C/C++로 작성된 파이썬 확장자는 글로벌 인터프리터 잠금의 영향을 받습니까?

Python의 가장 큰 장점 중 하나는 코드의 프로세서 집약적인 부분의 속도를 높이기 위해 C와 C++ 확장자를 쉽게 쓸 수 있다는 것입니다.이러한 확장 기능은 글로벌 인터프리터 잠금 기능을 피할 수 있는 것일까요, 아니면 GIL의 제한도 받을 수 있을까요? 그렇지 않다면, 이러한 "확장 용이성"은 이전에 생각했던 것보다 훨씬 더 치명적인 기능입니다.저는 단순한 예/아니오가 답이 아니라고 생각하지만 확신할 수 없어서 스택 오버플로우에 대한 질문을 드립니다.

예, 확장(Python에서 호출된 C 루틴)에 대한 호출은 여전히 GIL의 대상입니다.

그러나 Python VM에 제어권을 반환하기 전에 다시 할당하도록 주의해야 하는 한 GIL을 C 확장 내에서 수동으로 해제할 수 있습니다.

하십시오 .Py_BEGIN_ALLOW_THREADS그리고.Py_END_ALLOW_THREADS매크로: http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock

Python에 대한 C/C++ 확장자는 GIL에 구속되지 않습니다.하지만, 여러분은 정말로 여러분이 무엇을 하고 있는지 알 필요가 있습니다.http://docs.python.org/c-api/init.html 에서:

글로벌 인터프리터 잠금은 포인터를 현재 스레드 상태로 보호하는 데 사용됩니다.잠금을 해제하고 스레드 상태를 저장할 때는 잠금을 해제하기 전에 현재 스레드 상태 포인터를 가져와야 합니다(다른 스레드가 즉시 잠금을 획득하고 자체 스레드 상태를 글로벌 변수에 저장할 수 있으므로).반대로, 잠금을 획득하고 스레드 상태를 복원할 때, 스레드 상태 포인터를 저장하기 전에 잠금을 획득해야 합니다.

왜 내가 이 일에 대해 그렇게 상세하게 얘기하고 있는 거지?C로 스레드를 생성할 때 글로벌 인터프리터 잠금이 없거나 스레드 상태 데이터 구조가 없기 때문입니다.이러한 스레드는 먼저 스레드 상태 데이터 구조를 생성한 다음 잠금을 획득하고 마지막으로 스레드 상태 포인터를 저장하여 부트스트랩을 실행해야 Python/C API 사용을 시작할 수 있습니다.작업이 완료되면 스레드 상태 포인터를 재설정하고 잠금을 해제한 후 마지막으로 스레드 상태 데이터 구조를 해제해야 합니다.

Check out Cython, it has similar syntax to Python but with a few constructs like "cdef", fast numpy access functions, and a "with nogil" statement (which does what it says).

If you’re writing your extension in C++, you can use RAII to easily and legibly write code manipulating the GIL. I use this pair of RAII structlets:

namespace py {

    namespace gil {

        struct release {
            PyThreadState* state;
            bool active;

            release()
                :state(PyEval_SaveThread()), active(true)
                {}

            ~release() { if (active) { restore(); } }

            void restore() {
                PyEval_RestoreThread(state);
                active = false;
            }
        };

        struct ensure {
            PyGILState_STATE* state;
            bool active;

            ensure()
                :state(PyGILState_Ensure()), active(true)
                {}

            ~ensure() { if (active) { restore(); } }

            void restore() {
                PyGILState_Release(state);
                active = false;
            }
        };

    }

}

… allowing the GIL to be toggled for a given block (in a semantic manner that may seem dimly familiar for any context-manager Pythonista fans):

PyObject* YourPythonExtensionFunction(PyObject* self, PyObject* args) {

    Py_SomeCAPICall(…);     /// generally, if it starts with Py* it needs the GIL
    Py_SomeOtherCall(…);    /// ... there are exceptions, see the docs

    {
        py::gil::release nogil;
        std::cout << "Faster and less block-y I/O" << std::endl
                  << "can run inside this block -" << std::endl
                  << "unimpeded by the GIL";
    }

    Py_EvenMoreAPICallsForWhichTheGILMustBeInPlace(…);

}

… Indeed, personally also I find the ease of extending Python, and the level of control one has over the internal structures and state, a killer feature.

ReferenceURL : https://stackoverflow.com/questions/651048/concurrency-are-python-extensions-written-in-c-c-affected-by-the-global-inter

반응형