Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python C Extension - Any harm in adding Py_BEGIN_ALLOW_THREADS around every pure C call?

It's recommended to wrap pure C functions that block on IO, or even pure C functions that spend substantial time in CPU, in Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS blocks.

Is there any harm to wrapping pretty much all pure C functions (with pure C arguments- no Python API objects), regardless of whether it blocks on IO or CPU for a long time, with Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS ?

For example, say you had the following function, where some_pure_c_non_io_func spends a small time on a CPU task. Is there any harm in releasing the GIL here?

static PyObject Foo_bar(Foo *self, PyObject *args, PyObject *kwargs)
{
    int i = 0;

    if (!PyArg_ParseTuple(args, "i", &i)) {
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
    some_pure_c_non_io_func(i);
    Py_END_ALLOW_THREADS

    Py_RETURN_NONE;
}
like image 651
Matthew Moisen Avatar asked Mar 13 '26 01:03

Matthew Moisen


1 Answers

I'm not totally sure, but from looking at the code it seems there's no harm. Several popular libraries do this for intensive CPU functions, notably Tensorflow and Pillow

Py_BEGIN_ALLOW_THREADS expands to

PyThreadState *_save;
_save = PyEval_SaveThread();

PyEval_SaveThread saves the current thread state in _save (with `_PyThreadState_Swap) and gives up the GIL

Then Py_END_ALLOW_THREADS expands to

PyEval_RestoreThread(_save);

which does the same thing in reverse (takes GIL and makes _save the current thread state).

like image 153
computergorl Avatar answered Mar 15 '26 21:03

computergorl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!