diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 40f8ca68c00b72..65a708783b9de2 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -858,9 +858,11 @@ _PyObject_IS_GC(PyObject *obj) && (type->tp_is_gc == NULL || type->tp_is_gc(obj))); } -// Fast inlined version of PyObject_Hash() -static inline Py_hash_t -_PyObject_HashFast(PyObject *op) +// Fast inlined version of PyObject_Hash(). Dictionaries are very +// likely to include string keys (class and instance attributes, +// json, ...) so we include a fast path for strings. +static inline Py_ALWAYS_INLINE Py_hash_t +_PyObject_HashDictKey(PyObject *op) { if (PyUnicode_CheckExact(op)) { Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED( diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 3ba48d5d9d3c64..15bc9af3887bfa 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2565,7 +2565,7 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping, if (key == NULL) break; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { goto done; } diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 928b905aaedb1b..eb55ada13a4074 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2281,7 +2281,7 @@ dict_getitem(PyObject *op, PyObject *key, const char *warnmsg) } PyDictObject *mp = (PyDictObject *)op; - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { PyErr_FormatUnraisable(warnmsg); return NULL; @@ -2349,7 +2349,7 @@ _PyDict_LookupIndex(PyDictObject *mp, PyObject *key) assert(PyDict_CheckExact((PyObject*)mp)); assert(PyUnicode_CheckExact(key)); - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); return -1; @@ -2446,7 +2446,7 @@ PyDict_GetItemRef(PyObject *op, PyObject *key, PyObject **result) return -1; } - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); *result = NULL; @@ -2462,7 +2462,7 @@ _PyDict_GetItemRef_Unicode_LockHeld(PyDictObject *op, PyObject *key, PyObject ** ASSERT_DICT_LOCKED(op); assert(PyUnicode_CheckExact(key)); - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); *result = NULL; @@ -2500,7 +2500,7 @@ PyDict_GetItemWithError(PyObject *op, PyObject *key) PyErr_BadInternalCall(); return NULL; } - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); return NULL; @@ -2571,7 +2571,7 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) Py_hash_t hash; PyObject *value; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { return NULL; } @@ -2595,7 +2595,7 @@ _PyDict_LoadGlobalStackRef(PyDictObject *globals, PyDictObject *builtins, PyObje Py_ssize_t ix; Py_hash_t hash; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { *res = PyStackRef_NULL; return; @@ -2658,7 +2658,7 @@ setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) assert(key); assert(value); assert(PyDict_Check(mp)); - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); Py_DECREF(key); @@ -2810,7 +2810,7 @@ int PyDict_DelItem(PyObject *op, PyObject *key) { assert(key); - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); return -1; @@ -3138,7 +3138,7 @@ pop_lock_held(PyObject *op, PyObject *key, PyObject **result) return 0; } - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); if (result) { @@ -3472,7 +3472,7 @@ dict_subscript(PyObject *self, PyObject *key) Py_hash_t hash; PyObject *value; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); return NULL; @@ -4353,7 +4353,7 @@ dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) Py_hash_t hash; Py_ssize_t ix; - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); return NULL; @@ -4386,7 +4386,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu return -1; } - hash = _PyObject_HashFast(key); + hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); if (result) { @@ -4816,7 +4816,7 @@ static PyMethodDef mapp_methods[] = { int PyDict_Contains(PyObject *op, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = _PyObject_HashDictKey(key); if (hash == -1) { dict_unhashable_type(key); return -1; @@ -6903,7 +6903,7 @@ int _PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value) { if (value == NULL) { - Py_hash_t hash = _PyObject_HashFast(name); + Py_hash_t hash = _PyObject_HashDictKey(name); if (hash == -1) { dict_unhashable_type(name); return -1; diff --git a/Objects/setobject.c b/Objects/setobject.c index 6e4fc5957cad7f..34b1b19753c3a5 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -231,7 +231,7 @@ set_unhashable_type(PyObject *key) int _PySet_AddTakeRef(PySetObject *so, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { set_unhashable_type(key); Py_DECREF(key); @@ -400,7 +400,7 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash) static int set_add_key(PySetObject *so, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { set_unhashable_type(key); return -1; @@ -411,7 +411,7 @@ set_add_key(PySetObject *so, PyObject *key) static int set_contains_key(PySetObject *so, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { set_unhashable_type(key); return -1; @@ -422,7 +422,7 @@ set_contains_key(PySetObject *so, PyObject *key) static int set_discard_key(PySetObject *so, PyObject *key) { - Py_hash_t hash = _PyObject_HashFast(key); + Py_hash_t hash = PyObject_Hash(key); if (hash == -1) { set_unhashable_type(key); return -1; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index fb33bc747d885b..5b666f49af4726 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3749,7 +3749,7 @@ solid_base(PyTypeObject *type) // or when __bases__ is re-assigned. Since the slots are read without atomic // operations and without locking, we can only safely update them while the // world is stopped. However, with the world stopped, we are very limited on -// which APIs can be safely used. For example, calling _PyObject_HashFast() +// which APIs can be safely used. For example, calling _PyObject_HashDictKey() // or _PyDict_GetItemRef_KnownHash() are not safe and can potentially cause // deadlocks. Hashing can be re-entrant and _PyDict_GetItemRef_KnownHash can // acquire a lock if the dictionary is not owned by the current thread, to @@ -5898,7 +5898,7 @@ PyObject_GetItemData(PyObject *obj) static PyObject * find_name_in_mro(PyTypeObject *type, PyObject *name, int *error) { - Py_hash_t hash = _PyObject_HashFast(name); + Py_hash_t hash = _PyObject_HashDictKey(name); if (hash == -1) { *error = -1; return NULL;