]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-95004: specialize access to enums and fix scaling on free-threading (#148184)
authorKumar Aditya <kumaraditya@python.org>
Tue, 7 Apr 2026 16:13:50 +0000 (21:43 +0530)
committerGitHub <noreply@github.com>
Tue, 7 Apr 2026 16:13:50 +0000 (21:43 +0530)
Co-authored-by: Ken Jin <kenjin4096@gmail.com>
Lib/test/test_opcache.py
Misc/NEWS.d/next/Core_and_Builtins/2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst [new file with mode: 0644]
Python/specialize.c
Tools/ftscalingbench/ftscalingbench.py

index 4ca108cd6ca43eb935d946a66aaf0d9ffd413a47..5f40ad1d7a0f24342f1e2119f1d35e5fc364a374 100644 (file)
@@ -2047,5 +2047,25 @@ class TestSpecializer(TestBase):
             sys.modules.pop("test_module_with_getattr", None)
 
 
+    @cpython_only
+    @requires_specialization
+    def test_load_attr_enum(self):
+        import enum
+
+        class Color(enum.IntEnum):
+            RED = 1
+            GREEN = 2
+            BLUE = 3
+
+        def load_enum_member():
+            for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
+                x = Color.RED
+                assert x == 1
+
+        load_enum_member()
+        self.assert_specialized(load_enum_member,
+                                "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK")
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst
new file mode 100644 (file)
index 0000000..a492982
--- /dev/null
@@ -0,0 +1 @@
+The specializing interpreter now specializes for :class:`enum.Enum` improving performance and scaling in free-threading. Patch by Kumar Aditya.
index 0fe225dcbb6b5fe4eda5c81a77d17b688cf80e37..bfa7b8148e46de35b919809a5ef0bbe0038148be 100644 (file)
@@ -1201,22 +1201,33 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr,
         }
     }
     switch (kind) {
-        case METHOD:
-        case NON_DESCRIPTOR:
-            #ifdef Py_GIL_DISABLED
-            if (!_PyObject_HasDeferredRefcount(descr)) {
-                SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED);
+        case MUTABLE:
+            // special case for enums which has Py_TYPE(descr) == cls
+            // so guarding on type version is sufficient
+            if (Py_TYPE(descr) != cls) {
+                SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MUTABLE_CLASS);
                 Py_XDECREF(descr);
                 return -1;
             }
-            #endif
-            write_u32(cache->type_version, tp_version);
+            if (Py_TYPE(descr)->tp_descr_get || Py_TYPE(descr)->tp_descr_set) {
+                SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR);
+                Py_XDECREF(descr);
+                return -1;
+            }
+            _Py_FALLTHROUGH;
+        case METHOD:
+        case NON_DESCRIPTOR:
+#ifdef Py_GIL_DISABLED
+            maybe_enable_deferred_ref_count(descr);
+#endif
             write_ptr(cache->descr, descr);
             if (metaclass_check) {
-                write_u32(cache->keys_version, meta_version);
+                write_u32(cache->keys_version, tp_version);
+                write_u32(cache->type_version, meta_version);
                 specialize(instr, LOAD_ATTR_CLASS_WITH_METACLASS_CHECK);
             }
             else {
+                write_u32(cache->type_version, tp_version);
                 specialize(instr, LOAD_ATTR_CLASS);
             }
             Py_XDECREF(descr);
index a3d87e1f855dcbd9e36eb9a543bf793ffcab83df..60f43b99c0f69dd67cf938e58ac5ebf5cd0e479d 100644 (file)
@@ -295,6 +295,20 @@ def setattr_non_interned():
         setattr(obj, f"{prefix}_c", None)
 
 
+from enum import Enum
+class MyEnum(Enum):
+    X = 1
+    Y = 2
+    Z = 3
+
+@register_benchmark
+def enum_attr():
+    for _ in range(1000 * WORK_SCALE):
+        MyEnum.X
+        MyEnum.Y
+        MyEnum.Z
+
+
 def bench_one_thread(func):
     t0 = time.perf_counter_ns()
     func()