Skip to content

Commit 0a51113

Browse files
authored
gh-141510: Optimize PyDict_Copy() for frozendict (#145509)
Implement fast-path for frozendict in PyDict_Copy().
1 parent 95f56b1 commit 0a51113

File tree

1 file changed

+41
-17
lines changed

1 file changed

+41
-17
lines changed

Objects/dictobject.c

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -896,24 +896,20 @@ free_values(PyDictValues *values, bool use_qsbr)
896896
PyMem_Free(values);
897897
}
898898

899-
/* Consumes a reference to the keys object */
900-
static PyObject *
901-
new_dict(PyDictKeysObject *keys, PyDictValues *values,
902-
Py_ssize_t used, int free_values_on_failure)
899+
static inline PyObject *
900+
new_dict_impl(PyDictObject *mp, PyDictKeysObject *keys,
901+
PyDictValues *values, Py_ssize_t used,
902+
int free_values_on_failure)
903903
{
904904
assert(keys != NULL);
905-
PyDictObject *mp = _Py_FREELIST_POP(PyDictObject, dicts);
906905
if (mp == NULL) {
907-
mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
908-
if (mp == NULL) {
909-
dictkeys_decref(keys, false);
910-
if (free_values_on_failure) {
911-
free_values(values, false);
912-
}
913-
return NULL;
906+
dictkeys_decref(keys, false);
907+
if (free_values_on_failure) {
908+
free_values(values, false);
914909
}
910+
return NULL;
915911
}
916-
assert(Py_IS_TYPE(mp, &PyDict_Type));
912+
917913
mp->ma_keys = keys;
918914
mp->ma_values = values;
919915
mp->ma_used = used;
@@ -923,6 +919,29 @@ new_dict(PyDictKeysObject *keys, PyDictValues *values,
923919
return (PyObject *)mp;
924920
}
925921

922+
/* Consumes a reference to the keys object */
923+
static PyObject *
924+
new_dict(PyDictKeysObject *keys, PyDictValues *values,
925+
Py_ssize_t used, int free_values_on_failure)
926+
{
927+
PyDictObject *mp = _Py_FREELIST_POP(PyDictObject, dicts);
928+
if (mp == NULL) {
929+
mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
930+
}
931+
assert(mp == NULL || Py_IS_TYPE(mp, &PyDict_Type));
932+
933+
return new_dict_impl(mp, keys, values, used, free_values_on_failure);
934+
}
935+
936+
/* Consumes a reference to the keys object */
937+
static PyObject *
938+
new_frozendict(PyDictKeysObject *keys, PyDictValues *values,
939+
Py_ssize_t used, int free_values_on_failure)
940+
{
941+
PyDictObject *mp = PyObject_GC_New(PyDictObject, &PyFrozenDict_Type);
942+
return new_dict_impl(mp, keys, values, used, free_values_on_failure);
943+
}
944+
926945
static PyObject *
927946
new_dict_with_shared_keys(PyDictKeysObject *keys)
928947
{
@@ -4313,8 +4332,7 @@ copy_lock_held(PyObject *o, int as_frozendict)
43134332

43144333
if (Py_TYPE(mp)->tp_iter == dict_iter &&
43154334
mp->ma_values == NULL &&
4316-
(mp->ma_used >= (mp->ma_keys->dk_nentries * 2) / 3) &&
4317-
!as_frozendict)
4335+
(mp->ma_used >= (mp->ma_keys->dk_nentries * 2) / 3))
43184336
{
43194337
/* Use fast-copy if:
43204338
@@ -4334,9 +4352,15 @@ copy_lock_held(PyObject *o, int as_frozendict)
43344352
if (keys == NULL) {
43354353
return NULL;
43364354
}
4337-
PyDictObject *new = (PyDictObject *)new_dict(keys, NULL, 0, 0);
4355+
PyDictObject *new;
4356+
if (as_frozendict) {
4357+
new = (PyDictObject *)new_frozendict(keys, NULL, 0, 0);
4358+
}
4359+
else {
4360+
new = (PyDictObject *)new_dict(keys, NULL, 0, 0);
4361+
}
43384362
if (new == NULL) {
4339-
/* In case of an error, `new_dict()` takes care of
4363+
/* In case of an error, new_dict()/new_frozendict() takes care of
43404364
cleaning up `keys`. */
43414365
return NULL;
43424366
}

0 commit comments

Comments
 (0)