bpo-40521: Make context free list per-interpreter (GH-20644)

Each interpreter now has its own context free list:

* Move context free list into PyInterpreterState.
* Add _Py_context_state structure.
* Add tstate parameter to _PyContext_ClearFreeList()
  and _PyContext_Fini().
* Pass tstate to clear_freelists().
This commit is contained in:
Victor Stinner 2020-06-05 02:56:37 +02:00 committed by GitHub
parent 78a02c2568
commit e005ead49b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 34 additions and 28 deletions

View File

@ -37,6 +37,6 @@ struct _pycontexttokenobject {
int _PyContext_Init(void); int _PyContext_Init(void);
void _PyContext_Fini(void); void _PyContext_Fini(PyThreadState *tstate);
#endif /* !Py_INTERNAL_CONTEXT_H */ #endif /* !Py_INTERNAL_CONTEXT_H */

View File

@ -171,7 +171,7 @@ extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
extern void _PyList_ClearFreeList(PyThreadState *tstate); extern void _PyList_ClearFreeList(PyThreadState *tstate);
extern void _PyDict_ClearFreeList(void); extern void _PyDict_ClearFreeList(void);
extern void _PyAsyncGen_ClearFreeLists(PyThreadState *tstate); extern void _PyAsyncGen_ClearFreeLists(PyThreadState *tstate);
extern void _PyContext_ClearFreeList(void); extern void _PyContext_ClearFreeList(PyThreadState *tstate);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -124,6 +124,12 @@ struct _Py_async_gen_state {
int asend_numfree; int asend_numfree;
}; };
struct _Py_context_state {
// List of free PyContext objects
PyContext *freelist;
int numfree;
};
/* interpreter state */ /* interpreter state */
@ -223,6 +229,7 @@ struct _is {
struct _Py_float_state float_state; struct _Py_float_state float_state;
struct _Py_frame_state frame; struct _Py_frame_state frame;
struct _Py_async_gen_state async_gen; struct _Py_async_gen_state async_gen;
struct _Py_context_state context;
/* Using a cache is very effective since typically only a single slice is /* Using a cache is very effective since typically only a single slice is
created and then deleted again. */ created and then deleted again. */

View File

@ -1,4 +1,4 @@
The tuple free lists, the empty tuple singleton, the list free list, the float The tuple free lists, the empty tuple singleton, the list free list, the float
free list, the slice cache, the frame free list, the asynchronous generator free list, the slice cache, the frame free list, the asynchronous generator
free lists are no longer shared by all interpreters: each interpreter now its free lists, and the context free list are no longer shared by all interpreters:
has own free lists and caches. each interpreter now its has own free lists and caches.

View File

@ -1023,16 +1023,15 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate,
* Clearing the free lists may give back memory to the OS earlier. * Clearing the free lists may give back memory to the OS earlier.
*/ */
static void static void
clear_freelists(void) clear_freelists(PyThreadState *tstate)
{ {
PyThreadState *tstate = _PyThreadState_GET();
_PyFrame_ClearFreeList(tstate); _PyFrame_ClearFreeList(tstate);
_PyTuple_ClearFreeList(tstate); _PyTuple_ClearFreeList(tstate);
_PyFloat_ClearFreeList(tstate); _PyFloat_ClearFreeList(tstate);
_PyList_ClearFreeList(tstate); _PyList_ClearFreeList(tstate);
_PyDict_ClearFreeList(); _PyDict_ClearFreeList();
_PyAsyncGen_ClearFreeLists(tstate); _PyAsyncGen_ClearFreeLists(tstate);
_PyContext_ClearFreeList(); _PyContext_ClearFreeList(tstate);
} }
// Show stats for objects in each generations // Show stats for objects in each generations
@ -1306,7 +1305,7 @@ collect(PyThreadState *tstate, int generation,
/* Clear free list only during the collection of the highest /* Clear free list only during the collection of the highest
* generation */ * generation */
if (generation == NUM_GENERATIONS-1) { if (generation == NUM_GENERATIONS-1) {
clear_freelists(); clear_freelists(tstate);
} }
if (_PyErr_Occurred(tstate)) { if (_PyErr_Occurred(tstate)) {

View File

@ -10,8 +10,6 @@
#define CONTEXT_FREELIST_MAXLEN 255 #define CONTEXT_FREELIST_MAXLEN 255
static PyContext *ctx_freelist = NULL;
static int ctx_freelist_len = 0;
#include "clinic/context.c.h" #include "clinic/context.c.h"
@ -334,11 +332,13 @@ class _contextvars.Context "PyContext *" "&PyContext_Type"
static inline PyContext * static inline PyContext *
_context_alloc(void) _context_alloc(void)
{ {
PyInterpreterState *interp = _PyInterpreterState_GET();
struct _Py_context_state *state = &interp->context;
PyContext *ctx; PyContext *ctx;
if (ctx_freelist_len) { if (state->numfree) {
ctx_freelist_len--; state->numfree--;
ctx = ctx_freelist; ctx = state->freelist;
ctx_freelist = (PyContext *)ctx->ctx_weakreflist; state->freelist = (PyContext *)ctx->ctx_weakreflist;
ctx->ctx_weakreflist = NULL; ctx->ctx_weakreflist = NULL;
_Py_NewReference((PyObject *)ctx); _Py_NewReference((PyObject *)ctx);
} }
@ -458,10 +458,12 @@ context_tp_dealloc(PyContext *self)
} }
(void)context_tp_clear(self); (void)context_tp_clear(self);
if (ctx_freelist_len < CONTEXT_FREELIST_MAXLEN) { PyInterpreterState *interp = _PyInterpreterState_GET();
ctx_freelist_len++; struct _Py_context_state *state = &interp->context;
self->ctx_weakreflist = (PyObject *)ctx_freelist; if (state->numfree < CONTEXT_FREELIST_MAXLEN) {
ctx_freelist = self; state->numfree++;
self->ctx_weakreflist = (PyObject *)state->freelist;
state->freelist = self;
} }
else { else {
Py_TYPE(self)->tp_free(self); Py_TYPE(self)->tp_free(self);
@ -1271,11 +1273,12 @@ get_token_missing(void)
void void
_PyContext_ClearFreeList(void) _PyContext_ClearFreeList(PyThreadState *tstate)
{ {
for (; ctx_freelist_len; ctx_freelist_len--) { struct _Py_context_state *state = &tstate->interp->context;
PyContext *ctx = ctx_freelist; for (; state->numfree; state->numfree--) {
ctx_freelist = (PyContext *)ctx->ctx_weakreflist; PyContext *ctx = state->freelist;
state->freelist = (PyContext *)ctx->ctx_weakreflist;
ctx->ctx_weakreflist = NULL; ctx->ctx_weakreflist = NULL;
PyObject_GC_Del(ctx); PyObject_GC_Del(ctx);
} }
@ -1283,10 +1286,10 @@ _PyContext_ClearFreeList(void)
void void
_PyContext_Fini(void) _PyContext_Fini(PyThreadState *tstate)
{ {
Py_CLEAR(_token_missing); Py_CLEAR(_token_missing);
_PyContext_ClearFreeList(); _PyContext_ClearFreeList(tstate);
_PyHamt_Fini(); _PyHamt_Fini();
} }

View File

@ -1273,10 +1273,7 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp)
} }
_PyAsyncGen_Fini(tstate); _PyAsyncGen_Fini(tstate);
_PyContext_Fini(tstate);
if (is_main_interp) {
_PyContext_Fini();
}
/* Cleanup Unicode implementation */ /* Cleanup Unicode implementation */
_PyUnicode_Fini(tstate); _PyUnicode_Fini(tstate);