Skip to content

Commit 5b64c58

Browse files
committed
pythongh-87729: improve hit rate of LOAD_SUPER_ATTR specialization
1 parent 7a7eaff commit 5b64c58

13 files changed

+375
-353
lines changed

Include/internal/pycore_code.h

+2-5
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ typedef struct {
5353

5454
typedef struct {
5555
uint16_t counter;
56-
uint16_t class_version[2];
57-
uint16_t self_type_version[2];
58-
uint16_t method[4];
5956
} _PySuperAttrCache;
6057

6158
#define INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR CACHE_ENTRIES(_PySuperAttrCache)
@@ -226,8 +223,8 @@ extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range);
226223

227224
/* Specialization functions */
228225

229-
extern void _Py_Specialize_LoadSuperAttr(PyObject *global_super, PyObject *cls, PyObject *self,
230-
_Py_CODEUNIT *instr, PyObject *name, int load_method);
226+
extern void _Py_Specialize_LoadSuperAttr(PyObject *global_super, PyObject *cls,
227+
_Py_CODEUNIT *instr, int load_method);
231228
extern void _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr,
232229
PyObject *name);
233230
extern void _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr,

Include/internal/pycore_opcode.h

+13-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_typeobject.h

-2
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,6 @@ PyAPI_DATA(PyTypeObject) _PyBufferWrapper_Type;
142142

143143
PyObject *
144144
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found);
145-
PyObject *
146-
_PySuper_LookupDescr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name);
147145

148146
#ifdef __cplusplus
149147
}

Include/opcode.h

+28-27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ def _write_atomic(path, data, mode=0o666):
442442
# Python 3.12b1 3526 (Add instrumentation support)
443443
# Python 3.12b1 3527 (Add LOAD_SUPER_ATTR)
444444
# Python 3.12b1 3528 (Add LOAD_SUPER_ATTR_METHOD specialization)
445+
# Python 3.12b1 3529 (Shrink the LOAD_SUPER_ATTR caches)
445446

446447
# Python 3.13 will start with 3550
447448

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

461-
MAGIC_NUMBER = (3528).to_bytes(2, 'little') + b'\r\n'
462+
MAGIC_NUMBER = (3529).to_bytes(2, 'little') + b'\r\n'
462463

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

Lib/opcode.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ def pseudo_op(name, op, real_ops):
369369
"FOR_ITER_GEN",
370370
],
371371
"LOAD_SUPER_ATTR": [
372+
"LOAD_SUPER_ATTR_ATTR",
372373
"LOAD_SUPER_ATTR_METHOD",
373374
],
374375
"LOAD_ATTR": [
@@ -446,9 +447,6 @@ def pseudo_op(name, op, real_ops):
446447
},
447448
"LOAD_SUPER_ATTR": {
448449
"counter": 1,
449-
"class_version": 2,
450-
"self_type_version": 2,
451-
"method": 4,
452450
},
453451
"LOAD_ATTR": {
454452
"counter": 1,

Objects/typeobject.c

-12
Original file line numberDiff line numberDiff line change
@@ -10140,18 +10140,6 @@ _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *me
1014010140
return res;
1014110141
}
1014210142

10143-
PyObject *
10144-
_PySuper_LookupDescr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name)
10145-
{
10146-
PyTypeObject *su_obj_type = supercheck(su_type, su_obj);
10147-
if (su_obj_type == NULL) {
10148-
return NULL;
10149-
}
10150-
PyObject *res = _super_lookup_descr(su_type, su_obj_type, name);
10151-
Py_DECREF(su_obj_type);
10152-
return res;
10153-
}
10154-
1015510143
static PyObject *
1015610144
super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
1015710145
{

Python/bytecodes.c

+31-9
Original file line numberDiff line numberDiff line change
@@ -1556,17 +1556,18 @@ dummy_func(
15561556

15571557
family(load_super_attr, INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR) = {
15581558
LOAD_SUPER_ATTR,
1559+
LOAD_SUPER_ATTR_ATTR,
15591560
LOAD_SUPER_ATTR_METHOD,
15601561
};
15611562

1562-
inst(LOAD_SUPER_ATTR, (unused/9, global_super, class, self -- res2 if (oparg & 1), res)) {
1563+
inst(LOAD_SUPER_ATTR, (unused/1, global_super, class, self -- res2 if (oparg & 1), res)) {
15631564
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
15641565
int load_method = oparg & 1;
15651566
#if ENABLE_SPECIALIZATION
15661567
_PySuperAttrCache *cache = (_PySuperAttrCache *)next_instr;
15671568
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
15681569
next_instr--;
1569-
_Py_Specialize_LoadSuperAttr(global_super, class, self, next_instr, name, load_method);
1570+
_Py_Specialize_LoadSuperAttr(global_super, class, next_instr, load_method);
15701571
DISPATCH_SAME_OPARG();
15711572
}
15721573
STAT_INC(LOAD_SUPER_ATTR, deferred);
@@ -1584,17 +1585,38 @@ dummy_func(
15841585
ERROR_IF(res == NULL, error);
15851586
}
15861587

1587-
inst(LOAD_SUPER_ATTR_METHOD, (unused/1, class_version/2, self_type_version/2, method/4, global_super, class, self -- res2, res)) {
1588+
inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super, class, self -- res2 if (oparg & 1), res)) {
1589+
assert(!(oparg & 1));
15881590
DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR);
15891591
DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR);
1590-
DEOPT_IF(((PyTypeObject *)class)->tp_version_tag != class_version, LOAD_SUPER_ATTR);
1591-
PyTypeObject *self_type = Py_TYPE(self);
1592-
DEOPT_IF(self_type->tp_version_tag != self_type_version, LOAD_SUPER_ATTR);
1593-
res2 = method;
1594-
res = self; // transfer ownership
1595-
Py_INCREF(res2);
1592+
STAT_INC(LOAD_SUPER_ATTR, hit);
1593+
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
1594+
res = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL);
1595+
ERROR_IF(res == NULL, error);
1596+
DECREF_INPUTS();
1597+
}
1598+
1599+
inst(LOAD_SUPER_ATTR_METHOD, (unused/1, global_super, class, self -- res2, res)) {
1600+
assert(oparg & 1);
1601+
DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR);
1602+
DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR);
1603+
STAT_INC(LOAD_SUPER_ATTR, hit);
1604+
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
1605+
int method_found = 0;
1606+
res2 = _PySuper_Lookup((PyTypeObject *)class, self, name, &method_found);
15961607
Py_DECREF(global_super);
15971608
Py_DECREF(class);
1609+
if (res2 == NULL) {
1610+
Py_DECREF(self);
1611+
ERROR_IF(true, error);
1612+
}
1613+
if (method_found) {
1614+
res = self; // transfer ownership
1615+
} else {
1616+
Py_DECREF(self);
1617+
res = res2;
1618+
res2 = NULL;
1619+
}
15981620
}
15991621

16001622
family(load_attr, INLINE_CACHE_ENTRIES_LOAD_ATTR) = {

0 commit comments

Comments
 (0)