gh-111968: remove redundant fetching of interpreter state in dict implementation (#136673)

This commit is contained in:
Kumar Aditya 2025-07-15 19:15:11 +05:30 committed by GitHub
parent 7e10a103df
commit a8f42e6e88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -217,7 +217,7 @@ set_values(PyDictObject *mp, PyDictValues *values)
#define LOAD_KEYS_NENTRIES(keys) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries)
#define INCREF_KEYS_FT(dk) dictkeys_incref(dk)
#define DECREF_KEYS_FT(dk, shared) dictkeys_decref(_PyInterpreterState_GET(), dk, shared)
#define DECREF_KEYS_FT(dk, shared) dictkeys_decref(dk, shared)
static inline void split_keys_entry_added(PyDictKeysObject *keys)
{
@ -380,8 +380,7 @@ equally good collision statistics, needed less code & used less memory.
*/
static int dictresize(PyInterpreterState *interp, PyDictObject *mp,
uint8_t log_newsize, int unicode);
static int dictresize(PyDictObject *mp, uint8_t log_newsize, int unicode);
static PyObject* dict_iter(PyObject *dict);
@ -444,7 +443,7 @@ dictkeys_incref(PyDictKeysObject *dk)
}
static inline void
dictkeys_decref(PyInterpreterState *interp, PyDictKeysObject *dk, bool use_qsbr)
dictkeys_decref(PyDictKeysObject *dk, bool use_qsbr)
{
if (FT_ATOMIC_LOAD_SSIZE_RELAXED(dk->dk_refcnt) < 0) {
assert(FT_ATOMIC_LOAD_SSIZE_RELAXED(dk->dk_refcnt) == _Py_DICT_IMMORTAL_INITIAL_REFCNT);
@ -753,7 +752,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
static PyDictKeysObject*
new_keys_object(PyInterpreterState *interp, uint8_t log2_size, bool unicode)
new_keys_object(uint8_t log2_size, bool unicode)
{
Py_ssize_t usable;
int log2_bytes;
@ -867,8 +866,7 @@ free_values(PyDictValues *values, bool use_qsbr)
/* Consumes a reference to the keys object */
static PyObject *
new_dict(PyInterpreterState *interp,
PyDictKeysObject *keys, PyDictValues *values,
new_dict(PyDictKeysObject *keys, PyDictValues *values,
Py_ssize_t used, int free_values_on_failure)
{
assert(keys != NULL);
@ -876,7 +874,7 @@ new_dict(PyInterpreterState *interp,
if (mp == NULL) {
mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
if (mp == NULL) {
dictkeys_decref(interp, keys, false);
dictkeys_decref(keys, false);
if (free_values_on_failure) {
free_values(values, false);
}
@ -894,7 +892,7 @@ new_dict(PyInterpreterState *interp,
}
static PyObject *
new_dict_with_shared_keys(PyInterpreterState *interp, PyDictKeysObject *keys)
new_dict_with_shared_keys(PyDictKeysObject *keys)
{
size_t size = shared_keys_usable_size(keys);
PyDictValues *values = new_values(size);
@ -905,7 +903,7 @@ new_dict_with_shared_keys(PyInterpreterState *interp, PyDictKeysObject *keys)
for (size_t i = 0; i < size; i++) {
values->values[i] = NULL;
}
return new_dict(interp, keys, values, 0, 1);
return new_dict(keys, values, 0, 1);
}
@ -971,9 +969,8 @@ clone_combined_dict_keys(PyDictObject *orig)
PyObject *
PyDict_New(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
/* We don't incref Py_EMPTY_KEYS here because it is immortal. */
return new_dict(interp, Py_EMPTY_KEYS, NULL, 0, 0);
return new_dict(Py_EMPTY_KEYS, NULL, 0, 0);
}
/* Search index of hash table from offset of entry table */
@ -1714,9 +1711,9 @@ find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash)
}
static int
insertion_resize(PyInterpreterState *interp, PyDictObject *mp, int unicode)
insertion_resize(PyDictObject *mp, int unicode)
{
return dictresize(interp, mp, calculate_log2_keysize(GROWTH_RATE(mp)), unicode);
return dictresize(mp, calculate_log2_keysize(GROWTH_RATE(mp)), unicode);
}
static inline int
@ -1725,7 +1722,7 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
{
if (mp->ma_keys->dk_usable <= 0) {
/* Need to resize. */
if (insertion_resize(interp, mp, 1) < 0) {
if (insertion_resize(mp, 1) < 0) {
return -1;
}
}
@ -1823,7 +1820,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
ASSERT_DICT_LOCKED(mp);
if (DK_IS_UNICODE(mp->ma_keys) && !PyUnicode_CheckExact(key)) {
if (insertion_resize(interp, mp, 0) < 0)
if (insertion_resize(mp, 0) < 0)
goto Fail;
assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
}
@ -1838,7 +1835,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
}
/* No space in shared keys. Resize and continue below. */
if (insertion_resize(interp, mp, 1) < 0) {
if (insertion_resize(mp, 1) < 0) {
goto Fail;
}
}
@ -1893,8 +1890,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
ASSERT_DICT_LOCKED(mp);
int unicode = PyUnicode_CheckExact(key);
PyDictKeysObject *newkeys = new_keys_object(
interp, PyDict_LOG_MINSIZE, unicode);
PyDictKeysObject *newkeys = new_keys_object(PyDict_LOG_MINSIZE, unicode);
if (newkeys == NULL) {
Py_DECREF(key);
Py_DECREF(value);
@ -1989,7 +1985,7 @@ This function supports:
- Generic -> Generic
*/
static int
dictresize(PyInterpreterState *interp, PyDictObject *mp,
dictresize(PyDictObject *mp,
uint8_t log2_newsize, int unicode)
{
PyDictKeysObject *oldkeys, *newkeys;
@ -2017,7 +2013,7 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
*/
/* Allocate a new table. */
newkeys = new_keys_object(interp, log2_newsize, unicode);
newkeys = new_keys_object(log2_newsize, unicode);
if (newkeys == NULL) {
return -1;
}
@ -2060,7 +2056,7 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
}
UNLOCK_KEYS(oldkeys);
set_keys(mp, newkeys);
dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
dictkeys_decref(oldkeys, IS_DICT_SHARED(mp));
set_values(mp, NULL);
if (oldvalues->embedded) {
assert(oldvalues->embedded == 1);
@ -2141,7 +2137,7 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
}
static PyObject *
dict_new_presized(PyInterpreterState *interp, Py_ssize_t minused, bool unicode)
dict_new_presized(Py_ssize_t minused, bool unicode)
{
const uint8_t log2_max_presize = 17;
const Py_ssize_t max_presize = ((Py_ssize_t)1) << log2_max_presize;
@ -2162,17 +2158,16 @@ dict_new_presized(PyInterpreterState *interp, Py_ssize_t minused, bool unicode)
log2_newsize = estimate_log2_keysize(minused);
}
new_keys = new_keys_object(interp, log2_newsize, unicode);
new_keys = new_keys_object(log2_newsize, unicode);
if (new_keys == NULL)
return NULL;
return new_dict(interp, new_keys, NULL, 0, 0);
return new_dict(new_keys, NULL, 0, 0);
}
PyObject *
_PyDict_NewPresized(Py_ssize_t minused)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return dict_new_presized(interp, minused, false);
return dict_new_presized(minused, false);
}
PyObject *
@ -2182,7 +2177,6 @@ _PyDict_FromItems(PyObject *const *keys, Py_ssize_t keys_offset,
{
bool unicode = true;
PyObject *const *ks = keys;
PyInterpreterState *interp = _PyInterpreterState_GET();
for (Py_ssize_t i = 0; i < length; i++) {
if (!PyUnicode_CheckExact(*ks)) {
@ -2192,7 +2186,7 @@ _PyDict_FromItems(PyObject *const *keys, Py_ssize_t keys_offset,
ks += keys_offset;
}
PyObject *dict = dict_new_presized(interp, length, unicode);
PyObject *dict = dict_new_presized(length, unicode);
if (dict == NULL) {
return NULL;
}
@ -2895,7 +2889,7 @@ clear_lock_held(PyObject *op)
if (oldvalues == NULL) {
set_keys(mp, Py_EMPTY_KEYS);
assert(oldkeys->dk_refcnt == 1);
dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
dictkeys_decref(oldkeys, IS_DICT_SHARED(mp));
}
else {
n = oldkeys->dk_nentries;
@ -2909,7 +2903,7 @@ clear_lock_held(PyObject *op)
set_values(mp, NULL);
set_keys(mp, Py_EMPTY_KEYS);
free_values(oldvalues, IS_DICT_SHARED(mp));
dictkeys_decref(interp, oldkeys, false);
dictkeys_decref(oldkeys, false);
}
}
ASSERT_CONSISTENT(mp);
@ -3161,7 +3155,7 @@ dict_dict_fromkeys(PyInterpreterState *interp, PyDictObject *mp,
uint8_t new_size = Py_MAX(
estimate_log2_keysize(PyDict_GET_SIZE(iterable)),
DK_LOG_SIZE(mp->ma_keys));
if (dictresize(interp, mp, new_size, unicode)) {
if (dictresize(mp, new_size, unicode)) {
Py_DECREF(mp);
return NULL;
}
@ -3186,7 +3180,7 @@ dict_set_fromkeys(PyInterpreterState *interp, PyDictObject *mp,
uint8_t new_size = Py_MAX(
estimate_log2_keysize(PySet_GET_SIZE(iterable)),
DK_LOG_SIZE(mp->ma_keys));
if (dictresize(interp, mp, new_size, 0)) {
if (dictresize(mp, new_size, 0)) {
Py_DECREF(mp);
return NULL;
}
@ -3298,11 +3292,11 @@ dict_dealloc(PyObject *self)
}
free_values(values, false);
}
dictkeys_decref(interp, keys, false);
dictkeys_decref(keys, false);
}
else if (keys != NULL) {
assert(keys->dk_refcnt == 1 || keys == Py_EMPTY_KEYS);
dictkeys_decref(interp, keys, false);
dictkeys_decref(keys, false);
}
if (Py_IS_TYPE(mp, &PyDict_Type)) {
_Py_FREELIST_FREE(dicts, mp, Py_TYPE(mp)->tp_free);
@ -3832,7 +3826,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
return -1;
ensure_shared_on_resize(mp);
dictkeys_decref(interp, mp->ma_keys, IS_DICT_SHARED(mp));
dictkeys_decref(mp->ma_keys, IS_DICT_SHARED(mp));
set_keys(mp, keys);
STORE_USED(mp, other->ma_used);
ASSERT_CONSISTENT(mp);
@ -3851,8 +3845,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
*/
if (USABLE_FRACTION(DK_SIZE(mp->ma_keys)) < other->ma_used) {
int unicode = DK_IS_UNICODE(other->ma_keys);
if (dictresize(interp, mp,
estimate_log2_keysize(mp->ma_used + other->ma_used),
if (dictresize(mp, estimate_log2_keysize(mp->ma_used + other->ma_used),
unicode)) {
return -1;
}
@ -4117,7 +4110,7 @@ copy_lock_held(PyObject *o)
if (keys == NULL) {
return NULL;
}
PyDictObject *new = (PyDictObject *)new_dict(interp, keys, NULL, 0, 0);
PyDictObject *new = (PyDictObject *)new_dict(keys, NULL, 0, 0);
if (new == NULL) {
/* In case of an error, `new_dict()` takes care of
cleaning up `keys`. */
@ -4362,7 +4355,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
}
if (!PyUnicode_CheckExact(key) && DK_IS_UNICODE(mp->ma_keys)) {
if (insertion_resize(interp, mp, 0) < 0) {
if (insertion_resize(mp, 0) < 0) {
if (result) {
*result = NULL;
}
@ -4386,7 +4379,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
}
/* No space in shared keys. Resize and continue below. */
if (insertion_resize(interp, mp, 1) < 0) {
if (insertion_resize(mp, 1) < 0) {
goto error;
}
}
@ -4555,7 +4548,7 @@ dict_popitem_impl(PyDictObject *self)
}
/* Convert split table to combined table */
if (_PyDict_HasSplitTable(self)) {
if (dictresize(interp, self, DK_LOG_SIZE(self->ma_keys), 1) < 0) {
if (dictresize(self, DK_LOG_SIZE(self->ma_keys), 1) < 0) {
Py_DECREF(res);
return NULL;
}
@ -6725,10 +6718,7 @@ dictvalues_reversed(PyObject *self, PyObject *Py_UNUSED(ignored))
PyDictKeysObject *
_PyDict_NewKeysForClass(PyHeapTypeObject *cls)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
PyDictKeysObject *keys = new_keys_object(
interp, NEXT_LOG2_SHARED_KEYS_MAX_SIZE, 1);
PyDictKeysObject *keys = new_keys_object(NEXT_LOG2_SHARED_KEYS_MAX_SIZE, 1);
if (keys == NULL) {
PyErr_Clear();
}
@ -6792,8 +6782,7 @@ _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp)
}
static PyDictObject *
make_dict_from_instance_attributes(PyInterpreterState *interp,
PyDictKeysObject *keys, PyDictValues *values)
make_dict_from_instance_attributes(PyDictKeysObject *keys, PyDictValues *values)
{
dictkeys_incref(keys);
Py_ssize_t used = 0;
@ -6804,7 +6793,7 @@ make_dict_from_instance_attributes(PyInterpreterState *interp,
used += 1;
}
}
PyDictObject *res = (PyDictObject *)new_dict(interp, keys, values, used, 0);
PyDictObject *res = (PyDictObject *)new_dict(keys, values, used, 0);
return res;
}
@ -6818,9 +6807,8 @@ _PyObject_MaterializeManagedDict_LockHeld(PyObject *obj)
PyDictValues *values = _PyObject_InlineValues(obj);
PyDictObject *dict;
if (values->valid) {
PyInterpreterState *interp = _PyInterpreterState_GET();
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
dict = make_dict_from_instance_attributes(interp, keys, values);
dict = make_dict_from_instance_attributes(keys, values);
}
else {
dict = (PyDictObject *)PyDict_New();
@ -6916,7 +6904,7 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
if (dict == NULL) {
// Make the dict but don't publish it in the object
// so that no one else will see it.
dict = make_dict_from_instance_attributes(PyInterpreterState_Get(), keys, values);
dict = make_dict_from_instance_attributes(keys, values);
if (dict == NULL ||
_PyDict_SetItem_LockHeld(dict, name, value) < 0) {
Py_XDECREF(dict);
@ -7449,11 +7437,10 @@ PyObject_ClearManagedDict(PyObject *obj)
"clearing an object managed dict");
/* Clear the dict */
Py_BEGIN_CRITICAL_SECTION(dict);
PyInterpreterState *interp = _PyInterpreterState_GET();
PyDictKeysObject *oldkeys = dict->ma_keys;
set_keys(dict, Py_EMPTY_KEYS);
dict->ma_values = NULL;
dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(dict));
dictkeys_decref(oldkeys, IS_DICT_SHARED(dict));
STORE_USED(dict, 0);
clear_inline_values(_PyObject_InlineValues(obj));
Py_END_CRITICAL_SECTION();
@ -7490,8 +7477,7 @@ ensure_managed_dict(PyObject *obj)
goto done;
}
#endif
dict = (PyDictObject *)new_dict_with_shared_keys(_PyInterpreterState_GET(),
CACHED_KEYS(tp));
dict = (PyDictObject *)new_dict_with_shared_keys(CACHED_KEYS(tp));
FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict,
(PyDictObject *)dict);
@ -7520,9 +7506,8 @@ ensure_nonmanaged_dict(PyObject *obj, PyObject **dictptr)
#endif
PyTypeObject *tp = Py_TYPE(obj);
if (_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE) && (cached = CACHED_KEYS(tp))) {
PyInterpreterState *interp = _PyInterpreterState_GET();
assert(!_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES));
dict = new_dict_with_shared_keys(interp, cached);
dict = new_dict_with_shared_keys(cached);
}
else {
dict = PyDict_New();
@ -7578,8 +7563,7 @@ _PyObjectDict_SetItem(PyTypeObject *tp, PyObject *obj, PyObject **dictptr,
void
_PyDictKeys_DecRef(PyDictKeysObject *keys)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
dictkeys_decref(interp, keys, false);
dictkeys_decref(keys, false);
}
static inline uint32_t