Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-94673: Add Per-Interpreter Storage for Static Builtin Types #95255

Merged
merged 30 commits into from
Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7843524
Add PyInterpreterState.types.
ericsnowcurrently Jul 8, 2022
09fd0be
Add PyInterpreterState.types.num_builtins_initialized.
ericsnowcurrently Jul 9, 2022
afbde1a
Hard-code _Py_NUM_STATIC_BUILTIN_TYPES.
ericsnowcurrently Jul 18, 2022
f3aa649
Store per-interpreter state for static builtin types.
ericsnowcurrently Jul 19, 2022
9a1cc36
Export _PyStaticType_GetState().
ericsnowcurrently Jul 19, 2022
375b2d9
Move _PyStaticType_GetState() down next to _PyStaticType_InitBuiltin().
ericsnowcurrently Jul 19, 2022
b0b32a6
make it compile
kumaraditya303 Jul 19, 2022
eb05d9b
fix test_sys
kumaraditya303 Jul 19, 2022
f5ebd23
Reset tp_static_builtin_index only if a static builtin type.
ericsnowcurrently Jul 20, 2022
d94818b
Factor out get_static_builtin_index() and set_static_builtin_index().
ericsnowcurrently Jul 20, 2022
06799d5
Reset state->type later on.
ericsnowcurrently Jul 21, 2022
d78f10d
Add some asserts.
ericsnowcurrently Jul 21, 2022
44edc49
Decrement num_builtins_initialized.
ericsnowcurrently Jul 21, 2022
bc7fa26
Verify the per-interpreter type state has been cleared.
ericsnowcurrently Jul 22, 2022
3f20f4a
Add tp_static_builtin_index to the docs.
ericsnowcurrently Jul 25, 2022
9f7775d
Revert "Add tp_static_builtin_index to the docs."
ericsnowcurrently Jul 25, 2022
cf04c2c
Update tp_flags sooner in _PyStaticType_InitBuiltin().
ericsnowcurrently Jul 25, 2022
38427a0
Move the state/index-related helpers to the top.
ericsnowcurrently Jul 26, 2022
d313dbd
Add static_builtin_index_is_set() and static_builtin_index_clear().
ericsnowcurrently Jul 26, 2022
b4bbfcb
Hide the 1-based indexing behind the helper functions.
ericsnowcurrently Jul 26, 2022
1bd26c4
Move and edit a comment.
ericsnowcurrently Jul 26, 2022
652fcc6
Factor out static_builtin_state_init() and static_builtin_state_clear().
ericsnowcurrently Jul 26, 2022
d558a46
Un-initialize the type if PyType_Ready() fails.
ericsnowcurrently Jul 26, 2022
39a8fc7
Drop an empty line.
ericsnowcurrently Jul 26, 2022
6019eba
Move a comment.
ericsnowcurrently Jul 26, 2022
f2f10f0
static_builtin_type_state -> static_builtin_state (and factor out sta…
ericsnowcurrently Jul 26, 2022
cea5d34
Pass the type to static_builtin_state_get().
ericsnowcurrently Jul 26, 2022
4348e3f
Handle non-builtin and non-static types in _PyStaticType_GetState().
ericsnowcurrently Jul 26, 2022
1c8b0c3
Use static_builtin_index_is_set() in static_builtin_index_get().
ericsnowcurrently Jul 26, 2022
ae2d440
Restrict _PyStaticType_GetState() to static builtin types.
ericsnowcurrently Jul 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ struct _typeobject {

destructor tp_finalize;
vectorcallfunc tp_vectorcall;
size_t tp_static_builtin_index; /* 0 means "not initialized" */
};

/* This struct is used by the specializer
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ struct _is {
struct _Py_exc_state exc_state;

struct ast_state ast;
struct type_cache type_cache;
struct types_state types;
struct callable_cache callable_cache;

/* The following fields are here to avoid allocation during init.
Expand Down
16 changes: 16 additions & 0 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,25 @@ struct type_cache {
#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 int _PyStaticType_InitBuiltin(PyTypeObject *type);
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);
extern void _PyStaticType_Dealloc(PyTypeObject *type);


Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1507,7 +1507,7 @@ def delx(self): del self.__x
check((1,2,3), vsize('') + 3*self.P)
# type
# static type: PyTypeObject
fmt = 'P2nPI13Pl4Pn9Pn12PIP'
fmt = 'P2nPI13Pl4Pn9Pn12PIPI'
s = vsize('2P' + fmt)
check(int, s)
# class
Expand Down
111 changes: 106 additions & 5 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,88 @@ slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);

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.
* if present, returns a pointer pointing to the first '('.
Expand Down Expand Up @@ -206,7 +288,7 @@ static struct type_cache*
get_type_cache(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return &interp->type_cache;
return &interp->types.type_cache;
}


Expand All @@ -225,7 +307,7 @@ type_cache_clear(struct type_cache *cache, PyObject *value)
void
_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++) {
struct type_cache_entry *entry = &cache->hashtable[i];
assert(entry->name == NULL);
Expand All @@ -242,7 +324,7 @@ _PyType_InitCache(PyInterpreterState *interp)
static unsigned int
_PyType_ClearCache(PyInterpreterState *interp)
{
struct type_cache *cache = &interp->type_cache;
struct type_cache *cache = &interp->types.type_cache;
#if MCACHE_STATS
size_t total = cache->hits + cache->collisions + cache->misses;
fprintf(stderr, "-- Method cache hits = %zd (%d%%)\n",
Expand Down Expand Up @@ -274,11 +356,17 @@ PyType_ClearCache(void)
void
_PyTypes_Fini(PyInterpreterState *interp)
{
struct type_cache *cache = &interp->type_cache;
struct type_cache *cache = &interp->types.type_cache;
type_cache_clear(cache, NULL);
if (_Py_IsMainInterpreter(interp)) {
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);
}
}


Expand Down Expand Up @@ -4247,6 +4335,8 @@ clear_static_tp_subclasses(PyTypeObject *type)
void
_PyStaticType_Dealloc(PyTypeObject *type)
{
assert(!(type->tp_flags & Py_TPFLAGS_HEAPTYPE));

type_dealloc_common(type);

Py_CLEAR(type->tp_dict);
Expand All @@ -4261,6 +4351,11 @@ _PyStaticType_Dealloc(PyTypeObject *type)
}

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. */
}
}


Expand Down Expand Up @@ -6679,7 +6774,13 @@ _PyStaticType_InitBuiltin(PyTypeObject *self)
{
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;
}


Expand Down