1414 MCACHE_MAX_ATTR_SIZE, since it might be a problem if very large
1515 strings are used as attribute names. */
1616#define MCACHE_MAX_ATTR_SIZE 100
17- #define MCACHE_SIZE_EXP 9
17+ #define MCACHE_SIZE_EXP 12
1818#define MCACHE_HASH (version , name_hash ) \
19- (((unsigned int)(version) * (unsigned int)(name_hash)) \
20- >> (8*sizeof(unsigned int) - MCACHE_SIZE_EXP))
19+ (((unsigned int)(version) ^ (unsigned int)(name_hash)) \
20+ & ((1 << MCACHE_SIZE_EXP) - 1))
21+
2122#define MCACHE_HASH_METHOD (type , name ) \
2223 MCACHE_HASH((type)->tp_version_tag, \
2324 ((PyASCIIObject *)(name))->hash)
@@ -35,6 +36,14 @@ struct method_cache_entry {
3536static struct method_cache_entry method_cache [1 << MCACHE_SIZE_EXP ];
3637static unsigned int next_version_tag = 0 ;
3738
39+ #define MCACHE_STATS 0
40+
41+ #if MCACHE_STATS
42+ static size_t method_cache_hits = 0 ;
43+ static size_t method_cache_misses = 0 ;
44+ static size_t method_cache_collisions = 0 ;
45+ #endif
46+
3847/* alphabetical order */
3948_Py_IDENTIFIER (__abstractmethods__ );
4049_Py_IDENTIFIER (__class__ );
@@ -165,6 +174,18 @@ PyType_ClearCache(void)
165174 Py_ssize_t i ;
166175 unsigned int cur_version_tag = next_version_tag - 1 ;
167176
177+ #if MCACHE_STATS
178+ size_t total = method_cache_hits + method_cache_collisions + method_cache_misses ;
179+ fprintf (stderr , "-- Method cache hits = %zd (%d%%)\n" ,
180+ method_cache_hits , (int ) (100.0 * method_cache_hits / total ));
181+ fprintf (stderr , "-- Method cache true misses = %zd (%d%%)\n" ,
182+ method_cache_misses , (int ) (100.0 * method_cache_misses / total ));
183+ fprintf (stderr , "-- Method cache collisions = %zd (%d%%)\n" ,
184+ method_cache_collisions , (int ) (100.0 * method_cache_collisions / total ));
185+ fprintf (stderr , "-- Method cache size = %zd KB\n" ,
186+ sizeof (method_cache ) / 1024 );
187+ #endif
188+
168189 for (i = 0 ; i < (1 << MCACHE_SIZE_EXP ); i ++ ) {
169190 method_cache [i ].version = 0 ;
170191 Py_CLEAR (method_cache [i ].name );
@@ -2708,8 +2729,12 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
27082729 /* fast path */
27092730 h = MCACHE_HASH_METHOD (type , name );
27102731 if (method_cache [h ].version == type -> tp_version_tag &&
2711- method_cache [h ].name == name )
2732+ method_cache [h ].name == name ) {
2733+ #if MCACHE_STATS
2734+ method_cache_hits ++ ;
2735+ #endif
27122736 return method_cache [h ].value ;
2737+ }
27132738 }
27142739
27152740 /* Look in tp_dict of types in MRO */
@@ -2743,6 +2768,13 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
27432768 method_cache [h ].version = type -> tp_version_tag ;
27442769 method_cache [h ].value = res ; /* borrowed */
27452770 Py_INCREF (name );
2771+ assert (((PyASCIIObject * )(name ))-> hash != -1 );
2772+ #if MCACHE_STATS
2773+ if (method_cache [h ].name != Py_None && method_cache [h ].name != name )
2774+ method_cache_collisions ++ ;
2775+ else
2776+ method_cache_misses ++ ;
2777+ #endif
27462778 Py_DECREF (method_cache [h ].name );
27472779 method_cache [h ].name = name ;
27482780 }
0 commit comments