@@ -20,20 +20,13 @@ class object "PyObject *" "&PyBaseObject_Type"
2020
2121#include "clinic/typeobject.c.h"
2222
23- /* bpo-40521: Type method cache is shared by all subinterpreters */
24- #ifndef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
25- # define MCACHE
26- #endif
27-
28- #ifdef MCACHE
29- /* Support type attribute cache */
23+ /* Support type attribute lookup cache */
3024
3125/* The cache can keep references to the names alive for longer than
3226 they normally would. This is why the maximum size is limited to
3327 MCACHE_MAX_ATTR_SIZE, since it might be a problem if very large
3428 strings are used as attribute names. */
3529#define MCACHE_MAX_ATTR_SIZE 100
36- #define MCACHE_SIZE_EXP 12
3730#define MCACHE_HASH (version , name_hash ) \
3831 (((unsigned int)(version) ^ (unsigned int)(name_hash)) \
3932 & ((1 << MCACHE_SIZE_EXP) - 1))
@@ -44,30 +37,16 @@ class object "PyObject *" "&PyBaseObject_Type"
4437#define MCACHE_CACHEABLE_NAME (name ) \
4538 PyUnicode_CheckExact(name) && \
4639 PyUnicode_IS_READY(name) && \
47- PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE
48-
49- struct method_cache_entry {
50- unsigned int version ;
51- PyObject * name ; /* reference to exactly a str or None */
52- PyObject * value ; /* borrowed */
53- };
40+ (PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE)
5441
55- static struct method_cache_entry method_cache [ 1 << MCACHE_SIZE_EXP ];
42+ // Used to set PyTypeObject.tp_version_tag
5643static unsigned int next_version_tag = 0 ;
57- #endif
5844
5945typedef struct PySlot_Offset {
6046 short subslot_offset ;
6147 short slot_offset ;
6248} PySlot_Offset ;
6349
64- #define MCACHE_STATS 0
65-
66- #if MCACHE_STATS
67- static size_t method_cache_hits = 0 ;
68- static size_t method_cache_misses = 0 ;
69- static size_t method_cache_collisions = 0 ;
70- #endif
7150
7251/* bpo-40521: Interned strings are shared by all subinterpreters */
7352#ifndef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
@@ -229,46 +208,93 @@ _PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_d
229208 return PyUnicode_FromStringAndSize (start , end - start );
230209}
231210
232- unsigned int
233- PyType_ClearCache (void )
211+
212+ static struct type_cache *
213+ get_type_cache (void )
234214{
235- #ifdef MCACHE
236- Py_ssize_t i ;
237- unsigned int cur_version_tag = next_version_tag - 1 ;
215+ PyInterpreterState * interp = _PyInterpreterState_GET ();
216+ return & interp -> type_cache ;
217+ }
218+
238219
220+ static void
221+ type_cache_clear (struct type_cache * cache , int use_none )
222+ {
223+ for (Py_ssize_t i = 0 ; i < (1 << MCACHE_SIZE_EXP ); i ++ ) {
224+ struct type_cache_entry * entry = & cache -> hashtable [i ];
225+ entry -> version = 0 ;
226+ if (use_none ) {
227+ // Set to None so _PyType_Lookup() can use Py_SETREF(),
228+ // rather than using slower Py_XSETREF().
229+ Py_XSETREF (entry -> name , Py_NewRef (Py_None ));
230+ }
231+ else {
232+ Py_CLEAR (entry -> name );
233+ }
234+ entry -> value = NULL ;
235+ }
236+
237+ // Mark all version tags as invalid
238+ PyType_Modified (& PyBaseObject_Type );
239+ }
240+
241+
242+ void
243+ _PyType_InitCache (PyInterpreterState * interp )
244+ {
245+ struct type_cache * cache = & interp -> type_cache ;
246+ for (Py_ssize_t i = 0 ; i < (1 << MCACHE_SIZE_EXP ); i ++ ) {
247+ struct type_cache_entry * entry = & cache -> hashtable [i ];
248+ assert (entry -> name == NULL );
249+
250+ entry -> version = 0 ;
251+ // Set to None so _PyType_Lookup() can use Py_SETREF(),
252+ // rather than using slower Py_XSETREF().
253+ entry -> name = Py_NewRef (Py_None );
254+ entry -> value = NULL ;
255+ }
256+ }
257+
258+
259+ static unsigned int
260+ _PyType_ClearCache (struct type_cache * cache )
261+ {
239262#if MCACHE_STATS
240- size_t total = method_cache_hits + method_cache_collisions + method_cache_misses ;
263+ size_t total = cache -> hits + cache -> collisions + cache -> misses ;
241264 fprintf (stderr , "-- Method cache hits = %zd (%d%%)\n" ,
242- method_cache_hits , (int ) (100.0 * method_cache_hits / total ));
265+ cache -> hits , (int ) (100.0 * cache -> hits / total ));
243266 fprintf (stderr , "-- Method cache true misses = %zd (%d%%)\n" ,
244- method_cache_misses , (int ) (100.0 * method_cache_misses / total ));
267+ cache -> misses , (int ) (100.0 * cache -> misses / total ));
245268 fprintf (stderr , "-- Method cache collisions = %zd (%d%%)\n" ,
246- method_cache_collisions , (int ) (100.0 * method_cache_collisions / total ));
269+ cache -> collisions , (int ) (100.0 * cache -> collisions / total ));
247270 fprintf (stderr , "-- Method cache size = %zd KiB\n" ,
248- sizeof (method_cache ) / 1024 );
271+ sizeof (cache -> hashtable ) / 1024 );
249272#endif
250273
251- for (i = 0 ; i < (1 << MCACHE_SIZE_EXP ); i ++ ) {
252- method_cache [i ].version = 0 ;
253- Py_CLEAR (method_cache [i ].name );
254- method_cache [i ].value = NULL ;
255- }
274+ unsigned int cur_version_tag = next_version_tag - 1 ;
256275 next_version_tag = 0 ;
257- /* mark all version tags as invalid */
258- PyType_Modified ( & PyBaseObject_Type );
276+ type_cache_clear ( cache , 0 );
277+
259278 return cur_version_tag ;
260- #else
261- return 0 ;
262- #endif
263279}
264280
281+
282+ unsigned int
283+ PyType_ClearCache (void )
284+ {
285+ struct type_cache * cache = get_type_cache ();
286+ return _PyType_ClearCache (cache );
287+ }
288+
289+
265290void
266- _PyType_Fini (void )
291+ _PyType_Fini (PyThreadState * tstate )
267292{
268- PyType_ClearCache ( );
293+ _PyType_ClearCache ( & tstate -> interp -> type_cache );
269294 clear_slotdefs ();
270295}
271296
297+
272298void
273299PyType_Modified (PyTypeObject * type )
274300{
@@ -370,9 +396,8 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
370396 Py_TPFLAGS_VALID_VERSION_TAG );
371397}
372398
373- #ifdef MCACHE
374399static int
375- assign_version_tag (PyTypeObject * type )
400+ assign_version_tag (struct type_cache * cache , PyTypeObject * type )
376401{
377402 /* Ensure that the tp_version_tag is valid and set
378403 Py_TPFLAGS_VALID_VERSION_TAG. To respect the invariant, this
@@ -393,31 +418,22 @@ assign_version_tag(PyTypeObject *type)
393418 /* for stress-testing: next_version_tag &= 0xFF; */
394419
395420 if (type -> tp_version_tag == 0 ) {
396- /* wrap-around or just starting Python - clear the whole
397- cache by filling names with references to Py_None.
398- Values are also set to NULL for added protection, as they
399- are borrowed reference */
400- for (i = 0 ; i < (1 << MCACHE_SIZE_EXP ); i ++ ) {
401- method_cache [i ].value = NULL ;
402- Py_INCREF (Py_None );
403- Py_XSETREF (method_cache [i ].name , Py_None );
404- }
405- /* mark all version tags as invalid */
406- PyType_Modified (& PyBaseObject_Type );
421+ // Wrap-around or just starting Python - clear the whole cache
422+ type_cache_clear (cache , 1 );
407423 return 1 ;
408424 }
425+
409426 bases = type -> tp_bases ;
410427 n = PyTuple_GET_SIZE (bases );
411428 for (i = 0 ; i < n ; i ++ ) {
412429 PyObject * b = PyTuple_GET_ITEM (bases , i );
413430 assert (PyType_Check (b ));
414- if (!assign_version_tag ((PyTypeObject * )b ))
431+ if (!assign_version_tag (cache , (PyTypeObject * )b ))
415432 return 0 ;
416433 }
417434 type -> tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG ;
418435 return 1 ;
419436}
420- #endif
421437
422438
423439static PyMemberDef type_members [] = {
@@ -3316,20 +3332,19 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
33163332 PyObject * res ;
33173333 int error ;
33183334
3319- #ifdef MCACHE
33203335 if (MCACHE_CACHEABLE_NAME (name ) &&
33213336 _PyType_HasFeature (type , Py_TPFLAGS_VALID_VERSION_TAG )) {
33223337 /* fast path */
33233338 unsigned int h = MCACHE_HASH_METHOD (type , name );
3324- if (method_cache [h ].version == type -> tp_version_tag &&
3325- method_cache [h ].name == name ) {
3339+ struct type_cache * cache = get_type_cache ();
3340+ struct type_cache_entry * entry = & cache -> hashtable [h ];
3341+ if (entry -> version == type -> tp_version_tag && entry -> name == name ) {
33263342#if MCACHE_STATS
3327- method_cache_hits ++ ;
3343+ cache -> hits ++ ;
33283344#endif
3329- return method_cache [ h ]. value ;
3345+ return entry -> value ;
33303346 }
33313347 }
3332- #endif
33333348
33343349 /* We may end up clearing live exceptions below, so make sure it's ours. */
33353350 assert (!PyErr_Occurred ());
@@ -3351,22 +3366,25 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
33513366 return NULL ;
33523367 }
33533368
3354- #ifdef MCACHE
3355- if (MCACHE_CACHEABLE_NAME (name ) && assign_version_tag (type )) {
3356- unsigned int h = MCACHE_HASH_METHOD (type , name );
3357- method_cache [h ].version = type -> tp_version_tag ;
3358- method_cache [h ].value = res ; /* borrowed */
3359- Py_INCREF (name );
3360- assert (((PyASCIIObject * )(name ))-> hash != -1 );
3369+ if (MCACHE_CACHEABLE_NAME (name )) {
3370+ struct type_cache * cache = get_type_cache ();
3371+ if (assign_version_tag (cache , type )) {
3372+ unsigned int h = MCACHE_HASH_METHOD (type , name );
3373+ struct type_cache_entry * entry = & cache -> hashtable [h ];
3374+ entry -> version = type -> tp_version_tag ;
3375+ entry -> value = res ; /* borrowed */
3376+ assert (((PyASCIIObject * )(name ))-> hash != -1 );
33613377#if MCACHE_STATS
3362- if (method_cache [h ].name != Py_None && method_cache [h ].name != name )
3363- method_cache_collisions ++ ;
3364- else
3365- method_cache_misses ++ ;
3378+ if (entry -> name != Py_None && entry -> name != name ) {
3379+ cache -> collisions ++ ;
3380+ }
3381+ else {
3382+ cache -> misses ++ ;
3383+ }
33663384#endif
3367- Py_SETREF (method_cache [h ].name , name );
3385+ Py_SETREF (entry -> name , Py_NewRef (name ));
3386+ }
33683387 }
3369- #endif
33703388 return res ;
33713389}
33723390
0 commit comments