Skip to content

Commit 2d5c312

Browse files
skirpichevntessore
andcommitted
pythongh-103951: enable optimization for fast attribute access on module subclasses
This patch relax specialization requirements from ``PyModule_CheckExact(op)`` to ``Py_TYPE(op)->tp_getattro != PyModule_Type.tp_getattro``. Benchmarks: ```py import pyperf import b import c runner = pyperf.Runner() runner.timeit('b.x', 'b.x', globals=globals()) runner.timeit('c.x', 'c.x', globals=globals()) ``` ```py x = 1 ``` ```py import sys, types x = 1 class _Foo(types.ModuleType): pass sys.modules[__name__].__class__ = _Foo ``` On the main: ``` $ python a.py -q b.x: Mean +- std dev: 50.2 ns +- 2.7 ns c.x: Mean +- std dev: 132 ns +- 7 ns ``` With the patch: ``` $ python a.py -q b.x: Mean +- std dev: 52.9 ns +- 3.6 ns c.x: Mean +- std dev: 52.6 ns +- 2.7 ns ``` Co-authored-by: Nicolas Tessore <[email protected]>
1 parent 260843d commit 2d5c312

File tree

6 files changed

+12
-3
lines changed

6 files changed

+12
-3
lines changed

Include/internal/pycore_moduleobject.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ extern int _PyModule_IsPossiblyShadowing(PyObject *);
1616

1717
extern int _PyModule_IsExtension(PyObject *obj);
1818

19+
static inline int
20+
_PyModule_HasSpecializedGetAttr(PyObject* op)
21+
{
22+
return Py_TYPE(op)->tp_getattro != PyModule_Type.tp_getattro;
23+
}
24+
1925
typedef struct {
2026
PyObject_HEAD
2127
PyObject *md_dict;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Relax optimization requirements to allow fast attribute access to module
2+
subclasses.

Python/bytecodes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2108,7 +2108,7 @@ dummy_func(
21082108

21092109
op(_CHECK_ATTR_MODULE, (dict_version/2, owner -- owner)) {
21102110
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
2111-
DEOPT_IF(!PyModule_CheckExact(owner_o));
2111+
DEOPT_IF(_PyModule_HasSpecializedGetAttr(owner_o));
21122112
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
21132113
assert(dict != NULL);
21142114
DEOPT_IF(dict->ma_keys->dk_version != dict_version);

Python/generated_cases.c.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1143,7 +1143,7 @@ _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *nam
11431143
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
11441144
fail = true;
11451145
}
1146-
else if (PyModule_CheckExact(owner)) {
1146+
else if (!_PyModule_HasSpecializedGetAttr(owner)) {
11471147
fail = specialize_module_load_attr(owner, instr, name);
11481148
}
11491149
else if (PyType_Check(owner)) {

Tools/cases_generator/analyzer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool:
598598
"_PyLong_Multiply",
599599
"_PyLong_Subtract",
600600
"_PyManagedDictPointer_IsValues",
601+
"_PyModule_HasSpecializedGetAttr",
601602
"_PyObject_GC_IS_TRACKED",
602603
"_PyObject_GC_MAY_BE_TRACKED",
603604
"_PyObject_GC_TRACK",

0 commit comments

Comments
 (0)