@@ -43,13 +43,46 @@ class object "PyObject *" "&PyBaseObject_Type"
4343 MCACHE_MAX_ATTR_SIZE, since it might be a problem if very large
4444 strings are used as attribute names. */
4545#define MCACHE_MAX_ATTR_SIZE 100
46- #define MCACHE_HASH (version , name_hash ) \
47- (((unsigned int)(version) ^ (unsigned int)(name_hash)) \
48- & ((1 << MCACHE_SIZE_EXP) - 1))
4946
50- #define MCACHE_HASH_METHOD (type , name ) \
51- MCACHE_HASH(FT_ATOMIC_LOAD_UINT32_RELAXED((type)->tp_version_tag), \
52- ((Py_ssize_t)(name)) >> 3)
47+ static inline unsigned int
48+ mcache_name_hash (PyTypeObject * type , PyObject * name )
49+ {
50+ unsigned int name_hash ;
51+ #if Py_GIL_DISABLED
52+ // Cache misses are relatively more expensive for the free-threaded build.
53+ // So we use the unicode string hash and unicode compare for caching
54+ // names. This allows caching of non-interned strings.
55+ if (PyUnicode_CheckExact (name )) {
56+ name_hash = (unsigned int )_PyObject_HashFast (name );
57+ }
58+ else {
59+ name_hash = 0 ;
60+ }
61+ #else
62+ // Use the pointer value of the string for the hash and the compare. This
63+ // is faster but non-interned strings can't use the cache.
64+ name_hash = ((Py_ssize_t )(name )) >> 3 ;
65+ #endif
66+ unsigned int version = FT_ATOMIC_LOAD_UINT32_RELAXED ((type )-> tp_version_tag );
67+ return (((unsigned int )(version ) ^ (unsigned int )(name_hash )) &
68+ ((1 << MCACHE_SIZE_EXP ) - 1 ));
69+ }
70+
71+ static inline int
72+ mcache_name_eq (PyObject * entry_name , PyObject * name )
73+ {
74+ #ifdef Py_GIL_DISABLED
75+ if (entry_name == NULL || entry_name == Py_None ) {
76+ return 0 ;
77+ }
78+ assert (PyUnicode_CheckExact (entry_name ));
79+ assert (PyUnicode_Check (name ));
80+ return _PyUnicode_Equal (entry_name , name );
81+ #else
82+ return entry_name == name ;
83+ #endif
84+ }
85+
5386#define MCACHE_CACHEABLE_NAME (name ) \
5487 PyUnicode_CheckExact(name) && \
5588 (PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE)
@@ -5721,7 +5754,7 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
57215754unsigned int
57225755_PyType_LookupStackRefAndVersion (PyTypeObject * type , PyObject * name , _PyStackRef * out )
57235756{
5724- unsigned int h = MCACHE_HASH_METHOD (type , name );
5757+ unsigned int h = mcache_name_hash (type , name );
57255758 struct type_cache * cache = get_type_cache ();
57265759 struct type_cache_entry * entry = & cache -> hashtable [h ];
57275760#ifdef Py_GIL_DISABLED
@@ -5731,7 +5764,7 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
57315764 uint32_t entry_version = _Py_atomic_load_uint32_acquire (& entry -> version );
57325765 uint32_t type_version = _Py_atomic_load_uint32_acquire (& type -> tp_version_tag );
57335766 if (entry_version == type_version &&
5734- _Py_atomic_load_ptr_relaxed (& entry -> name ) == name ) {
5767+ mcache_name_eq ( _Py_atomic_load_ptr_relaxed (& entry -> name ), name ) ) {
57355768 OBJECT_STAT_INC_COND (type_cache_hits , !is_dunder_name (name ));
57365769 OBJECT_STAT_INC_COND (type_cache_dunder_hits , is_dunder_name (name ));
57375770 if (_Py_TryXGetStackRef (& entry -> value , out )) {
@@ -5752,7 +5785,7 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
57525785 }
57535786 }
57545787#else
5755- if (entry -> version == type -> tp_version_tag && entry -> name == name ) {
5788+ if (entry -> version == type -> tp_version_tag && mcache_name_eq ( entry -> name , name ) ) {
57565789 assert (type -> tp_version_tag );
57575790 OBJECT_STAT_INC_COND (type_cache_hits , !is_dunder_name (name ));
57585791 OBJECT_STAT_INC_COND (type_cache_dunder_hits , is_dunder_name (name ));
0 commit comments