8000 gh-94673: Add Per-Interpreter Storage for Static Builtin Types (#95255) · python/cpython@47e75a0 · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 47e75a0

Browse files
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.
1 parent 7ac5bb3 commit 47e75a0

File tree

5 files changed

+125
-7
lines changed

5 files changed

+125
-7
lines changed

Include/cpython/object.h

+1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ struct _typeobject {
227227

228228
destructor tp_finalize;
229229
vectorcallfunc tp_vectorcall;
230+
size_t tp_static_builtin_index; /* 0 means "not initialized" */
230231
};
231232

232233
/* This struct is used by the specializer

Include/internal/pycore_interp.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ struct _is {
173173
struct _Py_exc_state exc_state;
174174

175175
struct ast_state ast;
176-
struct type_cache type_cache;
176+
struct types_state types;
177177
struct callable_cache callable_cache;
178178

179179
/* The following fields are here to avoid allocation during init.

Include/internal/pycore_typeobject.h

+16
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,25 @@ struct type_cache {
3939
#endif
4040
};
4141

42+
/* For now we hard-code this to a value for which we are confident
43+
all the static builtin types will fit (for all builds). */
44+
#define _Py_MAX_STATIC_BUILTIN_TYPES 200
45+
46+
typedef struct {
47+
PyTypeObject *type;
48+
} static_builtin_state;
49+
50+
struct types_state {
51+
struct type_cache type_cache;
52+
size_t num_builtins_initialized;
53+
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
54+
};
55+
56+
4257
extern PyStatus _PyTypes_InitSlotDefs(void);
4358

4459
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
60+
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);
4561
extern void _PyStaticType_Dealloc(PyTypeObject *type);
4662

4763

Lib/test/test_sys.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1507,7 +1507,7 @@ def delx(self): del self.__x
15071507
check((1,2,3), vsize('') + 3*self.P)
15081508
# type
15091509
# static type: PyTypeObject
1510-
fmt = 'P2nPI13Pl4Pn9Pn12PIP'
1510+
fmt = 'P2nPI13Pl4Pn9Pn12PIPI'
15111511
s = vsize('2P' + fmt)
15121512
check(int, s)
15131513
# class

Objects/typeobject.c

+106-5
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,88 @@ slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);
6767

6868
static inline PyTypeObject * subclass_from_ref(PyObject *ref);
6969

70+
71+
/* helpers for for static builtin types */
72+
73+
static inline int
74+
static_builtin_index_is_set(PyTypeObject *self)
75+
{
76+
return self->tp_static_builtin_index > 0;
77+
}
78+
79+
static inline size_t
80+
static_builtin_index_get(PyTypeObject *self)
81+
{
82+
assert(static_builtin_index_is_set(self));
83+
/* We store a 1-based index so 0 can mean "not initialized". */
84+
return self->tp_static_builtin_index - 1;
85+
}
86+
87+
static inline void
88+
static_builtin_index_set(PyTypeObject *self, size_t index)
89+
{
90+
assert(index < _Py_MAX_STATIC_BUILTIN_TYPES);
91+
/* We store a 1-based index so 0 can mean "not initialized". */
92+
self->tp_static_builtin_index = index + 1;
93+
}
94+
95+
static inline void
96+
static_builtin_index_clear(PyTypeObject *self)
97+
{
98+
self->tp_static_builtin_index = 0;
99+
}
100+
101+
static inline static_builtin_state *
102+
static_builtin_state_get(PyInterpreterState *interp, PyTypeObject *self)
103+
{
104+
return &(interp->types.builtins[static_builtin_index_get(self)]);
105+
}
106+
107+
/* For static types we store some state in an array on each interpreter. */
108+
static_builtin_state *
109+
_PyStaticType_GetState(PyTypeObject *self)
110+
{
111+
assert(self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN);
112+
PyInterpreterState *interp = _PyInterpreterState_GET();
113+
return static_builtin_state_get(interp, self);
114+
}
115+
116+
static void
117+
static_builtin_state_init(PyTypeObject *self)
118+
{
119+
/* Set the type's per-interpreter state. */
120+
PyInterpreterState *interp = _PyInterpreterState_GET();
121+
122+
/* It should only be called once for each builtin type. */
123+
assert(!static_builtin_index_is_set(self));
124+
125+
static_builtin_index_set(self, interp->types.num_builtins_initialized);
126+
interp->types.num_builtins_initialized++;
127+
128+
static_builtin_state *state = static_builtin_state_get(interp, self);
129+
state->type = self;
130+
}
131+
132+
static void
133+
static_builtin_state_clear(PyTypeObject *self)
134+
{
135+
/* Reset the type's per-interpreter state.
136+
This basically undoes what static_builtin_state_init() did. */
137+
PyInterpreterState *interp = _PyInterpreterState_GET();
138+
139+
static_builtin_state *state = static_builtin_state_get(interp, self);
140+
state->type = NULL;
141+
static_builtin_index_clear(self);
142+
143+
assert(interp->types.num_builtins_initialized > 0);
144+
interp->types.num_builtins_initialized--;
145+
}
146+
147+
// Also see _PyStaticType_InitBuiltin() and _PyStaticType_Dealloc().
148+
149+
/* end static builtin helpers */
150+
151+
70152
/*
71153
* finds the beginning of the docstring's introspection signature.
72154
* if present, returns a pointer pointing to the first '('.
@@ -206,7 +288,7 @@ static struct type_cache*
206288
get_type_cache(void)
207289
{
208290
PyInterpreterState *interp = _PyInterpreterState_GET();
209-
return &interp->type_cache;
291+
return &interp->types.type_cache;
210292
}
211293

212294

@@ -225,7 +307,7 @@ type_cache_clear(struct type_cache *cache, PyObject *value)
225307
void
226308
_PyType_InitCache(PyInterpreterState *interp)
227309
{
228-
struct type_cache *cache = &interp->type_cache;
310+
struct type_cache *cache = &interp->types.type_cache;
229311
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
230312
struct type_cache_entry *entry = &cache->hashtable[i];
231313
assert(entry->name == NULL);
@@ -242,7 +324,7 @@ _PyType_InitCache(PyInterpreterState *interp)
242324
static unsigned int
243325
_PyType_ClearCache(PyInterpreterState *interp)
244326
{
245-
struct type_cache *cache = &interp->type_cache;
327+
struct type_cache *cache = &interp->types.type_cache;
246328
#if MCACHE_STATS
247329
size_t total = cache->hits + cache->collisions + cache->misses;
248330
fprintf(stderr, "-- Method cache hits = %zd (%d%%)\n",
@@ -274,11 +356,17 @@ PyType_ClearCache(void)
274356
void
275357
_PyTypes_Fini(PyInterpreterState *interp)
276358
{
277-
struct type_cache *cache = &interp->type_cache;
359+
struct type_cache *cache = &interp->types.type_cache;
278360
type_cache_clear(cache, NULL);
279361
if (_Py_IsMainInterpreter(interp)) {
280362
clear_slotdefs();
281363
}
364+
365+
assert(interp->types.num_builtins_initialized == 0);
366+
// All the static builtin types should have been finalized already.
367+
for (size_t i = 0; i < _Py_MAX_STATIC_BUILTIN_TYPES; i++) {
368+
assert(interp->types.builtins[i].type == NULL);
369+
}
282370
}
283371

284372

@@ -4247,6 +4335,8 @@ clear_static_tp_subclasses(PyTypeObject *type)
42474335
void
42484336
_PyStaticType_Dealloc(PyTypeObject *type)
42494337
{
4338+
assert(!(type->tp_flags & Py_TPFLAGS_HEAPTYPE));
4339+
42504340
type_dealloc_common(type);
42514341

42524342
Py_CLEAR(type->tp_dict);
@@ -4261,6 +4351,11 @@ _PyStaticType_Dealloc(PyTypeObject *type)
42614351
}
42624352

42634353
type->tp_flags &= ~Py_TPFLAGS_READY;
4354+
4355+
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
4356+
static_builtin_state_clear(type);
4357+
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
4358+
}
42644359
}
42654360

42664361

@@ -6679,7 +6774,13 @@ _PyStaticType_InitBuiltin(PyTypeObject *self)
66796774
{
66806775
self->tp_flags = self->tp_flags | _Py_TPFLAGS_STATIC_BUILTIN;
66816776

6682-
return PyType_Ready(self);
6777+
static_builtin_state_init(self);
6778+
6779+
int res = PyType_Ready(self);
6780+
if (res < 0) {
6781+
static_builtin_state_clear(self);
6782+
}
6783+
return res;
66836784
}
66846785

66856786

0 commit comments

Comments
 (0)
0