diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst new file mode 100644 index 00000000000000..b031fb3c75dea7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst @@ -0,0 +1,2 @@ +Optimize :meth:`!frozendict.fromkeys` to avoid unnecessary thread-safety operations +in frozendict cases. Patch by Donghee Na. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 46b0148cf59ab5..1c61db68b385a0 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2671,10 +2671,8 @@ _PyDict_LoadBuiltinsFromGlobals(PyObject *globals) /* Consumes references to key and value */ static int -setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) +anydict_setitem_take2(PyDictObject *mp, PyObject *key, PyObject *value) { - ASSERT_DICT_LOCKED(mp); - assert(key); assert(value); assert(PyAnyDict_Check(mp)); @@ -2693,6 +2691,14 @@ setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) return insertdict(mp, key, hash, value); } +/* Consumes references to key and value */ +static int +setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) +{ + ASSERT_DICT_LOCKED(mp); + return anydict_setitem_take2(mp, key, value); +} + int _PyDict_SetItem_Take2(PyDictObject *mp, PyObject *key, PyObject *value) { @@ -3284,8 +3290,8 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return NULL; - if (PyAnyDict_CheckExact(d)) { - if (PyAnyDict_CheckExact(iterable)) { + if (PyDict_CheckExact(d)) { + if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; Py_BEGIN_CRITICAL_SECTION2(d, iterable); @@ -3293,6 +3299,14 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) Py_END_CRITICAL_SECTION2(); return d; } + else if (PyFrozenDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(d); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } else if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; @@ -3302,6 +3316,29 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return d; } } + else if (PyFrozenDict_CheckExact(d)) { + if (PyDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(iterable); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } + else if (PyFrozenDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + return d; + } + else if (PyAnySet_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(iterable); + d = (PyObject *)dict_set_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } + } it = PyObject_GetIter(iterable); if (it == NULL){ @@ -3309,7 +3346,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return NULL; } - if (PyAnyDict_CheckExact(d)) { + if (PyDict_CheckExact(d)) { Py_BEGIN_CRITICAL_SECTION(d); while ((key = PyIter_Next(it)) != NULL) { status = setitem_lock_held((PyDictObject *)d, key, value); @@ -3321,7 +3358,19 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) } dict_iter_exit:; Py_END_CRITICAL_SECTION(); - } else { + } + else if (PyFrozenDict_CheckExact(d)) { + while ((key = PyIter_Next(it)) != NULL) { + // anydict_setitem_take2 consumes a reference to key + status = anydict_setitem_take2((PyDictObject *)d, + key, Py_NewRef(value)); + if (status < 0) { + assert(PyErr_Occurred()); + goto Fail; + } + } + } + else { while ((key = PyIter_Next(it)) != NULL) { status = PyObject_SetItem(d, key, value); Py_DECREF(key);