mirror of https://github.com/python/cpython
gh-94673: Add Per-Interpreter Storage for Static Builtin Types (#95255)
This is the last precursor to storing tp_subclasses (and tp_weaklist) on the interpreter state for static builtin types. Here we add per-type storage on PyInterpreterState, but only for the static builtin types. This involves the following: * add PyInterpreterState.types * move PyInterpreterState.type_cache to it * add a "num_builtins_initialized" field * add a "builtins" field (a static array big enough for all the static builtin types) * add _PyStaticType_GetState() to look up a static builtin type's state * (temporarily) add PyTypeObject.tp_static_builtin_index (to hold the type's index into PyInterpreterState.types.builtins) We will be eliminating tp_static_builtin_index in a later change.
This commit is contained in:
parent
7ac5bb3e6a
commit
47e75a0025
|
@ -227,6 +227,7 @@ struct _typeobject {
|
||||||
|
|
||||||
destructor tp_finalize;
|
destructor tp_finalize;
|
||||||
vectorcallfunc tp_vectorcall;
|
vectorcallfunc tp_vectorcall;
|
||||||
|
size_t tp_static_builtin_index; /* 0 means "not initialized" */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This struct is used by the specializer
|
/* This struct is used by the specializer
|
||||||
|
|
|
@ -173,7 +173,7 @@ struct _is {
|
||||||
struct _Py_exc_state exc_state;
|
struct _Py_exc_state exc_state;
|
||||||
|
|
||||||
struct ast_state ast;
|
struct ast_state ast;
|
||||||
struct type_cache type_cache;
|
struct types_state types;
|
||||||
struct callable_cache callable_cache;
|
struct callable_cache callable_cache;
|
||||||
|
|
||||||
/* The following fields are here to avoid allocation during init.
|
/* The following fields are here to avoid allocation during init.
|
||||||
|
|
|
@ -39,9 +39,25 @@ struct type_cache {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* For now we hard-code this to a value for which we are confident
|
||||||
|
all the static builtin types will fit (for all builds). */
|
||||||
|
#define _Py_MAX_STATIC_BUILTIN_TYPES 200
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyTypeObject *type;
|
||||||
|
} static_builtin_state;
|
||||||
|
|
||||||
|
struct types_state {
|
||||||
|
struct type_cache type_cache;
|
||||||
|
size_t num_builtins_initialized;
|
||||||
|
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
extern PyStatus _PyTypes_InitSlotDefs(void);
|
extern PyStatus _PyTypes_InitSlotDefs(void);
|
||||||
|
|
||||||
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
|
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
|
||||||
|
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);
|
||||||
extern void _PyStaticType_Dealloc(PyTypeObject *type);
|
extern void _PyStaticType_Dealloc(PyTypeObject *type);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1507,7 +1507,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
check((1,2,3), vsize('') + 3*self.P)
|
check((1,2,3), vsize('') + 3*self.P)
|
||||||
# type
|
# type
|
||||||
# static type: PyTypeObject
|
# static type: PyTypeObject
|
||||||
fmt = 'P2nPI13Pl4Pn9Pn12PIP'
|
fmt = 'P2nPI13Pl4Pn9Pn12PIPI'
|
||||||
s = vsize('2P' + fmt)
|
s = vsize('2P' + fmt)
|
||||||
check(int, s)
|
check(int, s)
|
||||||
# class
|
# class
|
||||||
|
|
|
@ -67,6 +67,88 @@ slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);
|
||||||
|
|
||||||
static inline PyTypeObject * subclass_from_ref(PyObject *ref);
|
static inline PyTypeObject * subclass_from_ref(PyObject *ref);
|
||||||
|
|
||||||
|
|
||||||
|
/* helpers for for static builtin types */
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
static_builtin_index_is_set(PyTypeObject *self)
|
||||||
|
{
|
||||||
|
return self->tp_static_builtin_index > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
static_builtin_index_get(PyTypeObject *self)
|
||||||
|
{
|
||||||
|
assert(static_builtin_index_is_set(self));
|
||||||
|
/* We store a 1-based index so 0 can mean "not initialized". */
|
||||||
|
return self->tp_static_builtin_index - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
static_builtin_index_set(PyTypeObject *self, size_t index)
|
||||||
|
{
|
||||||
|
assert(index < _Py_MAX_STATIC_BUILTIN_TYPES);
|
||||||
|
/* We store a 1-based index so 0 can mean "not initialized". */
|
||||||
|
self->tp_static_builtin_index = index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
static_builtin_index_clear(PyTypeObject *self)
|
||||||
|
{
|
||||||
|
self->tp_static_builtin_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline static_builtin_state *
|
||||||
|
static_builtin_state_get(PyInterpreterState *interp, PyTypeObject *self)
|
||||||
|
{
|
||||||
|
return &(interp->types.builtins[static_builtin_index_get(self)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For static types we store some state in an array on each interpreter. */
|
||||||
|
static_builtin_state *
|
||||||
|
_PyStaticType_GetState(PyTypeObject *self)
|
||||||
|
{
|
||||||
|
assert(self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN);
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
return static_builtin_state_get(interp, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
static_builtin_state_init(PyTypeObject *self)
|
||||||
|
{
|
||||||
|
/* Set the type's per-interpreter state. */
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
|
||||||
|
/* It should only be called once for each builtin type. */
|
||||||
|
assert(!static_builtin_index_is_set(self));
|
||||||
|
|
||||||
|
static_builtin_index_set(self, interp->types.num_builtins_initialized);
|
||||||
|
interp->types.num_builtins_initialized++;
|
||||||
|
|
||||||
|
static_builtin_state *state = static_builtin_state_get(interp, self);
|
||||||
|
state->type = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
static_builtin_state_clear(PyTypeObject *self)
|
||||||
|
{
|
||||||
|
/* Reset the type's per-interpreter state.
|
||||||
|
This basically undoes what static_builtin_state_init() did. */
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
|
||||||
|
static_builtin_state *state = static_builtin_state_get(interp, self);
|
||||||
|
state->type = NULL;
|
||||||
|
static_builtin_index_clear(self);
|
||||||
|
|
||||||
|
assert(interp->types.num_builtins_initialized > 0);
|
||||||
|
interp->types.num_builtins_initialized--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also see _PyStaticType_InitBuiltin() and _PyStaticType_Dealloc().
|
||||||
|
|
||||||
|
/* end static builtin helpers */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* finds the beginning of the docstring's introspection signature.
|
* finds the beginning of the docstring's introspection signature.
|
||||||
* if present, returns a pointer pointing to the first '('.
|
* if present, returns a pointer pointing to the first '('.
|
||||||
|
@ -206,7 +288,7 @@ static struct type_cache*
|
||||||
get_type_cache(void)
|
get_type_cache(void)
|
||||||
{
|
{
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
return &interp->type_cache;
|
return &interp->types.type_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -225,7 +307,7 @@ type_cache_clear(struct type_cache *cache, PyObject *value)
|
||||||
void
|
void
|
||||||
_PyType_InitCache(PyInterpreterState *interp)
|
_PyType_InitCache(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
struct type_cache *cache = &interp->type_cache;
|
struct type_cache *cache = &interp->types.type_cache;
|
||||||
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
|
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
|
||||||
struct type_cache_entry *entry = &cache->hashtable[i];
|
struct type_cache_entry *entry = &cache->hashtable[i];
|
||||||
assert(entry->name == NULL);
|
assert(entry->name == NULL);
|
||||||
|
@ -242,7 +324,7 @@ _PyType_InitCache(PyInterpreterState *interp)
|
||||||
static unsigned int
|
static unsigned int
|
||||||
_PyType_ClearCache(PyInterpreterState *interp)
|
_PyType_ClearCache(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
struct type_cache *cache = &interp->type_cache;
|
struct type_cache *cache = &interp->types.type_cache;
|
||||||
#if MCACHE_STATS
|
#if MCACHE_STATS
|
||||||
size_t total = cache->hits + cache->collisions + cache->misses;
|
size_t total = cache->hits + cache->collisions + cache->misses;
|
||||||
fprintf(stderr, "-- Method cache hits = %zd (%d%%)\n",
|
fprintf(stderr, "-- Method cache hits = %zd (%d%%)\n",
|
||||||
|
@ -274,11 +356,17 @@ PyType_ClearCache(void)
|
||||||
void
|
void
|
||||||
_PyTypes_Fini(PyInterpreterState *interp)
|
_PyTypes_Fini(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
struct type_cache *cache = &interp->type_cache;
|
struct type_cache *cache = &interp->types.type_cache;
|
||||||
type_cache_clear(cache, NULL);
|
type_cache_clear(cache, NULL);
|
||||||
if (_Py_IsMainInterpreter(interp)) {
|
if (_Py_IsMainInterpreter(interp)) {
|
||||||
clear_slotdefs();
|
clear_slotdefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(interp->types.num_builtins_initialized == 0);
|
||||||
|
// All the static builtin types should have been finalized already.
|
||||||
|
for (size_t i = 0; i < _Py_MAX_STATIC_BUILTIN_TYPES; i++) {
|
||||||
|
assert(interp->types.builtins[i].type == NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4247,6 +4335,8 @@ clear_static_tp_subclasses(PyTypeObject *type)
|
||||||
void
|
void
|
||||||
_PyStaticType_Dealloc(PyTypeObject *type)
|
_PyStaticType_Dealloc(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
|
assert(!(type->tp_flags & Py_TPFLAGS_HEAPTYPE));
|
||||||
|
|
||||||
type_dealloc_common(type);
|
type_dealloc_common(type);
|
||||||
|
|
||||||
Py_CLEAR(type->tp_dict);
|
Py_CLEAR(type->tp_dict);
|
||||||
|
@ -4261,6 +4351,11 @@ _PyStaticType_Dealloc(PyTypeObject *type)
|
||||||
}
|
}
|
||||||
|
|
||||||
type->tp_flags &= ~Py_TPFLAGS_READY;
|
type->tp_flags &= ~Py_TPFLAGS_READY;
|
||||||
|
|
||||||
|
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
|
||||||
|
static_builtin_state_clear(type);
|
||||||
|
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6679,7 +6774,13 @@ _PyStaticType_InitBuiltin(PyTypeObject *self)
|
||||||
{
|
{
|
||||||
self->tp_flags = self->tp_flags | _Py_TPFLAGS_STATIC_BUILTIN;
|
self->tp_flags = self->tp_flags | _Py_TPFLAGS_STATIC_BUILTIN;
|
||||||
|
|
||||||
return PyType_Ready(self);
|
static_builtin_state_init(self);
|
||||||
|
|
||||||
|
int res = PyType_Ready(self);
|
||||||
|
if (res < 0) {
|
||||||
|
static_builtin_state_clear(self);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue