diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-28-19-13-52.gh-issue-126096.3x4spF.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-28-19-13-52.gh-issue-126096.3x4spF.rst new file mode 100644 index 00000000000000..1e67a8ccefa1f7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-28-19-13-52.gh-issue-126096.3x4spF.rst @@ -0,0 +1 @@ +Optimized switching between interpreters via a freelist. diff --git a/Python/pystate.c b/Python/pystate.c index 36b31f3b9e4200..662480cb2f2098 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1390,10 +1390,26 @@ allocate_chunk(int size_in_bytes, _PyStackChunk* previous) return res; } +#ifdef HAVE_THREAD_LOCAL +_Py_thread_local static const struct _Py_freelist _Py_tss_tstates_freelist = { 0 }; +#define _PyThreadState_FREELIST() ((struct _Py_freelist *) &_Py_tss_tstates_freelist) +#endif + static _PyThreadStateImpl * alloc_threadstate(void) { +#ifdef HAVE_THREAD_LOCAL + _PyThreadStateImpl *impl = _PyFreeList_PopMem(_PyThreadState_FREELIST()); + if (impl == NULL) + { + return PyMem_RawCalloc(1, sizeof(_PyThreadStateImpl)); + } + // Zero-out the allocation again. + memset(impl, 0, sizeof(_PyThreadStateImpl)); + return impl; +#else return PyMem_RawCalloc(1, sizeof(_PyThreadStateImpl)); +#endif } static void @@ -1408,7 +1424,12 @@ free_threadstate(_PyThreadStateImpl *tstate) sizeof(*tstate)); } else { +#ifdef HAVE_THREAD_LOCAL + // Maximum of 100 thread states in the freelist + _PyFreeList_Free(_PyThreadState_FREELIST(), tstate, 100, PyMem_RawFree); +#else PyMem_RawFree(tstate); +#endif } } diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 2605825d3d0078..664559d0fef054 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -185,6 +185,7 @@ Python/pyfpe.c - PyFPE_counter - Python/import.c - pkgcontext - Python/pystate.c - _Py_tss_tstate - +Python/pystate.c - _Py_tss_tstates_freelist - ##----------------------- ## should be const