[Python-checkins] bpo-44914: Maintain invariants of type version tags. (GH-27773)

markshannon webhook-mailer at python.org
Mon Aug 16 07:21:55 EDT 2021


https://github.com/python/cpython/commit/1a511dc92dd10ee8fc2e5da9f52f795924bdc89a
commit: 1a511dc92dd10ee8fc2e5da9f52f795924bdc89a
branch: main
author: Mark Shannon <mark at hotpy.org>
committer: markshannon <mark at hotpy.org>
date: 2021-08-16T12:21:34+01:00
summary:

bpo-44914: Maintain invariants of type version tags. (GH-27773)

* Do not invalidate type versions unnecessarily.

files:
A Misc/NEWS.d/next/Core and Builtins/2021-08-16-11-36-02.bpo-44914.6Lgrx3.rst
M Objects/typeobject.c

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.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 7ae50c453ed2f8..1efb9fc5944b02 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;
@@ -233,24 +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, 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;
-        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);
-        }
+        Py_XSETREF(entry->name, _Py_XNewRef(value));
         entry->value = NULL;
     }
-
-    // Mark all version tags as invalid
-    PyType_Modified(&PyBaseObject_Type);
 }
 
 
@@ -287,14 +277,11 @@ _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;
-    }
+    // 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);
 
-    type_cache_clear(cache, 0);
-
-    return cur_version_tag;
+    return next_version_tag - 1;
 }
 
 
@@ -309,7 +296,8 @@ PyType_ClearCache(void)
 void
 _PyType_Fini(PyInterpreterState *interp)
 {
-    _PyType_ClearCache(interp);
+    struct type_cache *cache = &interp->type_cache;
+    type_cache_clear(cache, NULL);
     if (_Py_IsMainInterpreter(interp)) {
         clear_slotdefs();
     }
@@ -426,14 +414,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);



More information about the Python-checkins mailing list