Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Lib/test/test_opcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -2047,5 +2047,25 @@ 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
assert x == 1

load_enum_member()
self.assert_specialized(load_enum_member,
"LOAD_ATTR_CLASS_WITH_METACLASS_CHECK")


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The specializing interpreter now specializes for :class:`enum.Enum` improving performance and scaling in free-threading. Patch by Kumar Aditya.
27 changes: 19 additions & 8 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions Tools/ftscalingbench/ftscalingbench.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading