Skip to content

Commit 7726245

Browse files
authored
gh-87729: improve hit rate of LOAD_SUPER_ATTR specialization (#104270)
1 parent ddc0e70 commit 7726245

13 files changed

+374
-352
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)
@@ -227,8 +224,8 @@ extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range);
227224

228225
/* Specialization functions */
229226

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

Include/internal/pycore_opcode.h

+12-12
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
@@ -443,6 +443,7 @@ def _write_atomic(path, data, mode=0o666):
443443
# Python 3.12b1 3527 (Add LOAD_SUPER_ATTR)
444444
# Python 3.12b1 3528 (Add LOAD_SUPER_ATTR_METHOD specialization)
445445
# Python 3.12b1 3529 (Inline list/dict/set comprehensions)
446+
# Python 3.12b1 3530 (Shrink the LOAD_SUPER_ATTR caches)
446447

447448
# Python 3.13 will start with 3550
448449

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

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

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

Lib/opcode.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ def pseudo_op(name, op, real_ops):
373373
"FOR_ITER_GEN",
374374
],
375375
"LOAD_SUPER_ATTR": [
376+
"LOAD_SUPER_ATTR_ATTR",
376377
"LOAD_SUPER_ATTR_METHOD",
377378
],
378379
"LOAD_ATTR": [
@@ -450,9 +451,6 @@ def pseudo_op(name, op, real_ops):
450451
},
451452
"LOAD_SUPER_ATTR": {
452453
"counter": 1,
453-
"class_version": 2,
454-
"self_type_version": 2,
455-
"method": 4,
456454
},
457455
"LOAD_ATTR": {
458456
"counter": 1,

Objects/typeobject.c

-12
Original file line numberDiff line numberDiff line change
@@ -10245,18 +10245,6 @@ _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *me
1024510245
return res;
1024610246
}
1024710247

10248-
PyObject *
10249-
_PySuper_LookupDescr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name)
10250-
{
10251-
PyTypeObject *su_obj_type = supercheck(su_type, su_obj);
10252-
if (su_obj_type == NULL) {
10253-
return NULL;
10254-
}
10255-
PyObject *res = _super_lookup_descr(su_type, su_obj_type, name);
10256-
Py_DECREF(su_obj_type);
10257-
return res;
10258-
}
10259-
1026010248
static PyObject *
1026110249
super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
1026210250
{

Python/bytecodes.c

+31-9
Original file line numberDiff line numberDiff line change
@@ -1562,17 +1562,18 @@ dummy_func(
15621562

15631563
family(load_super_attr, INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR) = {
15641564
LOAD_SUPER_ATTR,
1565+
LOAD_SUPER_ATTR_ATTR,
15651566
LOAD_SUPER_ATTR_METHOD,
15661567
};
15671568

1568-
inst(LOAD_SUPER_ATTR, (unused/9, global_super, class, self -- res2 if (oparg & 1), res)) {
1569+
inst(LOAD_SUPER_ATTR, (unused/1, global_super, class, self -- res2 if (oparg & 1), res)) {
15691570
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
15701571
int load_method = oparg & 1;
15711572
#if ENABLE_SPECIALIZATION
15721573
_PySuperAttrCache *cache = (_PySuperAttrCache *)next_instr;
15731574
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
15741575
next_instr--;
1575-
_Py_Specialize_LoadSuperAttr(global_super, class, self, next_instr, name, load_method);
1576+
_Py_Specialize_LoadSuperAttr(global_super, class, next_instr, load_method);
15761577
DISPATCH_SAME_OPARG();
15771578
}
15781579
STAT_INC(LOAD_SUPER_ATTR, deferred);
@@ -1590,17 +1591,38 @@ dummy_func(
15901591
ERROR_IF(res == NULL, error);
15911592
}
15921593

1593-
inst(LOAD_SUPER_ATTR_METHOD, (unused/1, class_version/2, self_type_version/2, method/4, global_super, class, self -- res2, res)) {
1594+
inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super, class, self -- res2 if (oparg & 1), res)) {
1595+
assert(!(oparg & 1));
15941596
DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR);
15951597
DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR);
1596-
DEOPT_IF(((PyTypeObject *)class)->tp_version_tag != class_version, LOAD_SUPER_ATTR);
1597-
PyTypeObject *self_type = Py_TYPE(self);
1598-
DEOPT_IF(self_type->tp_version_tag != self_type_version, LOAD_SUPER_ATTR);
1599-
res2 = method;
1600-
res = self; // transfer ownership
1601-
Py_INCREF(res2);
1598+
STAT_INC(LOAD_SUPER_ATTR, hit);
1599+
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
1600+
res = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL);
1601+
ERROR_IF(res == NULL, error);
1602+
DECREF_INPUTS();
1603+
}
1604+
1605+
inst(LOAD_SUPER_ATTR_METHOD, (unused/1, global_super, class, self -- res2, res)) {
1606+
assert(oparg & 1);
1607+
DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR);
1608+
DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR);
1609+
STAT_INC(LOAD_SUPER_ATTR, hit);
1610+
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
1611+
int method_found = 0;
1612+
res2 = _PySuper_Lookup((PyTypeObject *)class, self, name, &method_found);
16021613
Py_DECREF(global_super);
16031614
Py_DECREF(class);
1615+
if (res2 == NULL) {
1616+
Py_DECREF(self);
1617+
ERROR_IF(true, error);
1618+
}
1619+
if (method_found) {
1620+
res = self; // transfer ownership
1621+
} else {
1622+
Py_DECREF(self);
1623+
res = res2;
1624+
res2 = NULL;
1625+
}
16041626
}
16051627

16061628
family(load_attr, INLINE_CACHE_ENTRIES_LOAD_ATTR) = {

0 commit comments

Comments
 (0)