Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-87729: improve hit rate of LOAD_SUPER_ATTR specialization #104270

Merged
merged 4 commits into from
May 11, 2023
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
7 changes: 2 additions & 5 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ typedef struct {

typedef struct {
uint16_t counter;
uint16_t class_version[2];
uint16_t self_type_version[2];
uint16_t method[4];
} _PySuperAttrCache;

#define INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR CACHE_ENTRIES(_PySuperAttrCache)
Expand Down Expand Up @@ -227,8 +224,8 @@ extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range);

/* Specialization functions */

extern void _Py_Specialize_LoadSuperAttr(PyObject *global_super, PyObject *cls, PyObject *self,
_Py_CODEUNIT *instr, PyObject *name, int load_method);
extern void _Py_Specialize_LoadSuperAttr(PyObject *global_super, PyObject *cls,
_Py_CODEUNIT *instr, int load_method);
extern void _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr,
PyObject *name);
extern void _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr,
Expand Down
24 changes: 12 additions & 12 deletions Include/internal/pycore_opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,6 @@ PyAPI_DATA(PyTypeObject) _PyBufferWrapper_Type;

PyObject *
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found);
PyObject *
_PySuper_LookupDescr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name);

#ifdef __cplusplus
}
Expand Down
55 changes: 28 additions & 27 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.12b1 3527 (Add LOAD_SUPER_ATTR)
# Python 3.12b1 3528 (Add LOAD_SUPER_ATTR_METHOD specialization)
# Python 3.12b1 3529 (Inline list/dict/set comprehensions)
# Python 3.12b1 3530 (Shrink the LOAD_SUPER_ATTR caches)

# Python 3.13 will start with 3550

Expand All @@ -459,7 +460,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3529).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3530).to_bytes(2, 'little') + b'\r\n'

_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

Expand Down
4 changes: 1 addition & 3 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ def pseudo_op(name, op, real_ops):
"FOR_ITER_GEN",
],
"LOAD_SUPER_ATTR": [
"LOAD_SUPER_ATTR_ATTR",
"LOAD_SUPER_ATTR_METHOD",
],
"LOAD_ATTR": [
Expand Down Expand Up @@ -450,9 +451,6 @@ def pseudo_op(name, op, real_ops):
},
"LOAD_SUPER_ATTR": {
"counter": 1,
"class_version": 2,
"self_type_version": 2,
"method": 4,
},
"LOAD_ATTR": {
"counter": 1,
Expand Down
12 changes: 0 additions & 12 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -10245,18 +10245,6 @@ _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *me
return res;
}

PyObject *
_PySuper_LookupDescr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name)
{
PyTypeObject *su_obj_type = supercheck(su_type, su_obj);
if (su_obj_type == NULL) {
return NULL;
}
PyObject *res = _super_lookup_descr(su_type, su_obj_type, name);
Py_DECREF(su_obj_type);
return res;
}

static PyObject *
super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
Expand Down
40 changes: 31 additions & 9 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1562,17 +1562,18 @@ dummy_func(

family(load_super_attr, INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR) = {
LOAD_SUPER_ATTR,
LOAD_SUPER_ATTR_ATTR,
LOAD_SUPER_ATTR_METHOD,
};

inst(LOAD_SUPER_ATTR, (unused/9, global_super, class, self -- res2 if (oparg & 1), res)) {
inst(LOAD_SUPER_ATTR, (unused/1, global_super, class, self -- res2 if (oparg & 1), res)) {
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
int load_method = oparg & 1;
#if ENABLE_SPECIALIZATION
_PySuperAttrCache *cache = (_PySuperAttrCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
next_instr--;
_Py_Specialize_LoadSuperAttr(global_super, class, self, next_instr, name, load_method);
_Py_Specialize_LoadSuperAttr(global_super, class, next_instr, load_method);
DISPATCH_SAME_OPARG();
}
STAT_INC(LOAD_SUPER_ATTR, deferred);
Expand All @@ -1590,17 +1591,38 @@ dummy_func(
ERROR_IF(res == NULL, error);
}

inst(LOAD_SUPER_ATTR_METHOD, (unused/1, class_version/2, self_type_version/2, method/4, global_super, class, self -- res2, res)) {
inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super, class, self -- res2 if (oparg & 1), res)) {
assert(!(oparg & 1));
DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR);
DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR);
DEOPT_IF(((PyTypeObject *)class)->tp_version_tag != class_version, LOAD_SUPER_ATTR);
PyTypeObject *self_type = Py_TYPE(self);
DEOPT_IF(self_type->tp_version_tag != self_type_version, LOAD_SUPER_ATTR);
res2 = method;
res = self; // transfer ownership
Py_INCREF(res2);
STAT_INC(LOAD_SUPER_ATTR, hit);
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
res = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL);
ERROR_IF(res == NULL, error);
DECREF_INPUTS();
}

inst(LOAD_SUPER_ATTR_METHOD, (unused/1, global_super, class, self -- res2, res)) {
assert(oparg & 1);
DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR);
DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR);
STAT_INC(LOAD_SUPER_ATTR, hit);
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
int method_found = 0;
res2 = _PySuper_Lookup((PyTypeObject *)class, self, name, &method_found);
Py_DECREF(global_super);
Py_DECREF(class);
if (res2 == NULL) {
Py_DECREF(self);
ERROR_IF(true, error);
}
if (method_found) {
res = self; // transfer ownership
} else {
Py_DECREF(self);
res = res2;
res2 = NULL;
}
}

family(load_attr, INLINE_CACHE_ENTRIES_LOAD_ATTR) = {
Expand Down
Loading