From f49a1ce8a60206d2d075a778d0f6c26ec81d5cee Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Sun, 15 Aug 2021 16:48:53 +0100 Subject: [PATCH 1/6] Do not invalidate type versions unnecessarily. --- Objects/typeobject.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 7ae50c453ed2f8..6cbce32d14bfb0 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -45,7 +45,7 @@ class object "PyObject *" "&PyBaseObject_Type" // bpo-42745: next_version_tag remains shared by all interpreters because of static types // Used to set PyTypeObject.tp_version_tag -static unsigned int next_version_tag = 0; +static unsigned int next_version_tag = 1; typedef struct PySlot_Offset { short subslot_offset; @@ -248,9 +248,6 @@ type_cache_clear(struct type_cache *cache, int use_none) } entry->value = NULL; } - - // Mark all version tags as invalid - PyType_Modified(&PyBaseObject_Type); } @@ -287,14 +284,9 @@ _PyType_ClearCache(PyInterpreterState *interp) sizeof(cache->hashtable) / 1024); #endif - unsigned int cur_version_tag = next_version_tag - 1; - if (_Py_IsMainInterpreter(interp)) { - next_version_tag = 0; - } - - type_cache_clear(cache, 0); + type_cache_clear(cache, 1); - return cur_version_tag; + return next_version_tag - 1; } @@ -426,14 +418,12 @@ assign_version_tag(struct type_cache *cache, PyTypeObject *type) if (!_PyType_HasFeature(type, Py_TPFLAGS_READY)) return 0; - type->tp_version_tag = next_version_tag++; - /* for stress-testing: next_version_tag &= 0xFF; */ - - if (type->tp_version_tag == 0) { - // Wrap-around or just starting Python - clear the whole cache - type_cache_clear(cache, 1); + if (next_version_tag == 0) { + /* We have run out of version numbers */ return 0; } + type->tp_version_tag = next_version_tag++; + assert (type->tp_version_tag != 0); bases = type->tp_bases; n = PyTuple_GET_SIZE(bases); From 68fd56eaaff8383d4569e757014085be8326c7d6 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Sun, 15 Aug 2021 16:55:40 +0100 Subject: [PATCH 2/6] Remove unused argument. --- Objects/typeobject.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 6cbce32d14bfb0..cc91e9e6c96a7a 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -233,19 +233,14 @@ get_type_cache(void) static void -type_cache_clear(struct type_cache *cache, int use_none) +type_cache_clear(struct type_cache *cache) { for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) { struct type_cache_entry *entry = &cache->hashtable[i]; entry->version = 0; - if (use_none) { - // Set to None so _PyType_Lookup() can use Py_SETREF(), - // rather than using slower Py_XSETREF(). - Py_XSETREF(entry->name, Py_NewRef(Py_None)); - } - else { - Py_CLEAR(entry->name); - } + // Set to None, rather than NULL, so _PyType_Lookup() can + // use Py_SETREF() rather than using slower Py_XSETREF(). + Py_XSETREF(entry->name, Py_NewRef(Py_None)); entry->value = NULL; } } @@ -284,7 +279,7 @@ _PyType_ClearCache(PyInterpreterState *interp) sizeof(cache->hashtable) / 1024); #endif - type_cache_clear(cache, 1); + type_cache_clear(cache); return next_version_tag - 1; } From 3850ef2a3eab0af5062bf9d690101c8364310ef1 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Sun, 15 Aug 2021 18:29:28 +0100 Subject: [PATCH 3/6] Clear references to names in method cache in _PyType_Fini. --- Objects/typeobject.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index cc91e9e6c96a7a..8e140310f4b19a 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -296,7 +296,10 @@ PyType_ClearCache(void) void _PyType_Fini(PyInterpreterState *interp) { - _PyType_ClearCache(interp); + struct type_cache *cache = &interp->type_cache; + for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) { + Py_DECREF(cache->hashtable[i].name); + } if (_Py_IsMainInterpreter(interp)) { clear_slotdefs(); } From 68da204fbc309eb5049bf3ee5132c92145e7ca69 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Sun, 15 Aug 2021 21:51:35 +0100 Subject: [PATCH 4/6] Make sure that type cache is fully cleared in _PyType_Fini. --- Objects/typeobject.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 8e140310f4b19a..69b8750211725b 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -298,7 +298,10 @@ _PyType_Fini(PyInterpreterState *interp) { struct type_cache *cache = &interp->type_cache; for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) { - Py_DECREF(cache->hashtable[i].name); + struct type_cache_entry *entry = &cache->hashtable[i]; + entry->version = 0; + Py_CLEAR(entry->name); + entry->value = NULL; } if (_Py_IsMainInterpreter(interp)) { clear_slotdefs(); From 4e6865a3216e8ad005df76418a079c0f74d79d72 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 16 Aug 2021 11:36:14 +0100 Subject: [PATCH 5/6] Add NEWS --- .../2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst b/Misc/NEWS.d/next/Core and Builtins/2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst new file mode 100644 index 00000000000000..5e306ffaf5de84 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst @@ -0,0 +1,5 @@ +Class version tags are no longer recycled. + +This means that a version tag serves as a unique identifier for the state of +a class. We rely on this for effective specialization of the LOAD_ATTR and +other instructions. From 6bbb1cf5fa1cb708040e03b5656c5c1355e19224 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 16 Aug 2021 11:46:23 +0100 Subject: [PATCH 6/6] Refactor code a bit to avoid duplication. --- Objects/typeobject.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 69b8750211725b..1efb9fc5944b02 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -233,14 +233,12 @@ get_type_cache(void) static void -type_cache_clear(struct type_cache *cache) +type_cache_clear(struct type_cache *cache, PyObject *value) { for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) { struct type_cache_entry *entry = &cache->hashtable[i]; entry->version = 0; - // Set to None, rather than NULL, so _PyType_Lookup() can - // use Py_SETREF() rather than using slower Py_XSETREF(). - Py_XSETREF(entry->name, Py_NewRef(Py_None)); + Py_XSETREF(entry->name, _Py_XNewRef(value)); entry->value = NULL; } } @@ -279,7 +277,9 @@ _PyType_ClearCache(PyInterpreterState *interp) sizeof(cache->hashtable) / 1024); #endif - type_cache_clear(cache); + // Set to None, rather than NULL, so _PyType_Lookup() can + // use Py_SETREF() rather than using slower Py_XSETREF(). + type_cache_clear(cache, Py_None); return next_version_tag - 1; } @@ -297,12 +297,7 @@ void _PyType_Fini(PyInterpreterState *interp) { struct type_cache *cache = &interp->type_cache; - for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) { - struct type_cache_entry *entry = &cache->hashtable[i]; - entry->version = 0; - Py_CLEAR(entry->name); - entry->value = NULL; - } + type_cache_clear(cache, NULL); if (_Py_IsMainInterpreter(interp)) { clear_slotdefs(); }