From 212bc3c0b62601496e9d452b97fa1d9345eab547 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 6 Apr 2026 20:00:50 +0530 Subject: [PATCH 1/6] fix scaling of enums --- Python/specialize.c | 20 ++++++++++++++------ Tools/ftscalingbench/ftscalingbench.py | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Python/specialize.c b/Python/specialize.c index 0fe225dcbb6b5f..b07f4cde2f5da2 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1201,15 +1201,23 @@ 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 + if (Py_TYPE(descr)->tp_descr_get || Py_TYPE(descr)->tp_descr_set) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MUTABLE_CLASS); + Py_XDECREF(descr); + return -1; + } + _Py_FALLTHROUGH; + case METHOD: + case NON_DESCRIPTOR: + PyUnstable_Object_EnableDeferredRefcount(descr); write_u32(cache->type_version, tp_version); write_ptr(cache->descr, descr); if (metaclass_check) { diff --git a/Tools/ftscalingbench/ftscalingbench.py b/Tools/ftscalingbench/ftscalingbench.py index a3d87e1f855dcb..b13dfc15ac3f43 100644 --- a/Tools/ftscalingbench/ftscalingbench.py +++ b/Tools/ftscalingbench/ftscalingbench.py @@ -295,6 +295,27 @@ 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 + +@register_benchmark +def enum_value(): + for _ in range(1000 * WORK_SCALE): + MyEnum.X.value + MyEnum.Y.value + MyEnum.Z.value + + def bench_one_thread(func): t0 = time.perf_counter_ns() func() From 48ec2706a4a79a4367cc8c7d39cebd2ab17338cb Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 6 Apr 2026 23:51:11 +0530 Subject: [PATCH 2/6] add tests --- Lib/test/test_opcache.py | 29 ++++++++++++++++++++++++++ Python/specialize.c | 6 ++++-- Tools/ftscalingbench/ftscalingbench.py | 7 ------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 4ca108cd6ca43e..4dfb63b5c668ca 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -2047,5 +2047,34 @@ def load_module_attr_missing(): 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 + self.assertEqual(x, 1) + + load_enum_member() + self.assert_specialized(load_enum_member, + "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK") + + def load_enum_value(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + x = Color.RED.value + Color.GREEN.value + self.assertEqual(x, 3) + + load_enum_value() + self.assert_specialized(load_enum_value, + "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK") + + if __name__ == "__main__": unittest.main() diff --git a/Python/specialize.c b/Python/specialize.c index b07f4cde2f5da2..cb3d96c0db1f3b 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1203,7 +1203,7 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, switch (kind) { case MUTABLE: // special case for enums which has Py_TYPE(descr) == cls - // so guarding on type_version is sufficient + // so guarding on type version is sufficient if (Py_TYPE(descr) != cls) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MUTABLE_CLASS); Py_XDECREF(descr); @@ -1217,7 +1217,9 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, _Py_FALLTHROUGH; case METHOD: case NON_DESCRIPTOR: - PyUnstable_Object_EnableDeferredRefcount(descr); +#ifdef Py_GIL_DISABLED + maybe_enable_deferred_ref_count(descr); +#endif write_u32(cache->type_version, tp_version); write_ptr(cache->descr, descr); if (metaclass_check) { diff --git a/Tools/ftscalingbench/ftscalingbench.py b/Tools/ftscalingbench/ftscalingbench.py index b13dfc15ac3f43..60f43b99c0f69d 100644 --- a/Tools/ftscalingbench/ftscalingbench.py +++ b/Tools/ftscalingbench/ftscalingbench.py @@ -308,13 +308,6 @@ def enum_attr(): MyEnum.Y MyEnum.Z -@register_benchmark -def enum_value(): - for _ in range(1000 * WORK_SCALE): - MyEnum.X.value - MyEnum.Y.value - MyEnum.Z.value - def bench_one_thread(func): t0 = time.perf_counter_ns() From 76e1ffcf09be9f7ff61b3eb45838cfb0e30c5424 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:26:02 +0000 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst 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 index 00000000000000..5193fb440ae2f7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst @@ -0,0 +1 @@ +The specializing interpreter now specializes for :class:`enum.Enum` improving performance and better scaling in free-threading. Patch by Kumar Aditya. From 410381613d1fa957e34d1c55f15faa681132f037 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Tue, 7 Apr 2026 00:02:15 +0530 Subject: [PATCH 4/6] address review --- Lib/test/test_opcache.py | 11 +---------- Python/specialize.c | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 4dfb63b5c668ca..5f40ad1d7a0f24 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -2060,21 +2060,12 @@ class Color(enum.IntEnum): def load_enum_member(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): x = Color.RED - self.assertEqual(x, 1) + assert x == 1 load_enum_member() self.assert_specialized(load_enum_member, "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK") - def load_enum_value(): - for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): - x = Color.RED.value + Color.GREEN.value - self.assertEqual(x, 3) - - load_enum_value() - self.assert_specialized(load_enum_value, - "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK") - if __name__ == "__main__": unittest.main() diff --git a/Python/specialize.c b/Python/specialize.c index cb3d96c0db1f3b..355b6eabdb7a5b 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1210,7 +1210,7 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, return -1; } if (Py_TYPE(descr)->tp_descr_get || Py_TYPE(descr)->tp_descr_set) { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MUTABLE_CLASS); + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR); Py_XDECREF(descr); return -1; } From fc85a0dd25ed607b63a6d85aa5588634c3ca8c22 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Tue, 7 Apr 2026 00:16:17 +0530 Subject: [PATCH 5/6] fix news wording --- .../2026-04-06-18-25-53.gh-issue-95004.CQeT_H.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 5193fb440ae2f7..a492982bc62da7 100644 --- 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 @@ -1 +1 @@ -The specializing interpreter now specializes for :class:`enum.Enum` improving performance and better scaling in free-threading. Patch by Kumar Aditya. +The specializing interpreter now specializes for :class:`enum.Enum` improving performance and scaling in free-threading. Patch by Kumar Aditya. From 2702e8bed8f58fa1ee9280296fe3470497062ad7 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Tue, 7 Apr 2026 04:24:17 +0800 Subject: [PATCH 6/6] Fix LOAD_ATTR_CLASS_WITH_METACLASS_CHECK cache --- Python/specialize.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Python/specialize.c b/Python/specialize.c index 355b6eabdb7a5b..bfa7b8148e46de 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1220,13 +1220,14 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, #ifdef Py_GIL_DISABLED maybe_enable_deferred_ref_count(descr); #endif - write_u32(cache->type_version, tp_version); 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);