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:
parent
78a02c2568
commit
e005ead49b
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue