Skip to content

Commit ef25feb

Browse files
authored
gh-87729: specialize LOAD_SUPER_ATTR_METHOD (#103809)
1 parent cef542c commit ef25feb

14 files changed

+540
-358
lines changed

Include/internal/pycore_code.h

+11
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ typedef struct {
5151

5252
#define INLINE_CACHE_ENTRIES_BINARY_SUBSCR CACHE_ENTRIES(_PyBinarySubscrCache)
5353

54+
typedef struct {
55+
uint16_t counter;
56+
uint16_t class_version[2];
57+
uint16_t self_type_version[2];
58+
uint16_t method[4];
59+
} _PySuperAttrCache;
60+
61+
#define INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR CACHE_ENTRIES(_PySuperAttrCache)
62+
5463
typedef struct {
5564
uint16_t counter;
5665
uint16_t version[2];
@@ -217,6 +226,8 @@ extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range);
217226

218227
/* Specialization functions */
219228

229+
extern void _Py_Specialize_LoadSuperAttr(PyObject *global_super, PyObject *class, PyObject *self,
230+
_Py_CODEUNIT *instr, PyObject *name, int load_method);
220231
extern void _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr,
221232
PyObject *name);
222233
extern void _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr,

Include/internal/pycore_opcode.h

+13-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
@@ -119,6 +119,8 @@ PyObject *_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name);
119119

120120
PyObject *
121121
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found);
122+
PyObject *
123+
_PySuper_LookupDescr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name);
122124

123125
#ifdef __cplusplus
124126
}

Include/opcode.h

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

Lib/importlib/_bootstrap_external.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ def _write_atomic(path, data, mode=0o666):
440440
# Python 3.12a7 3524 (Shrink the BINARY_SUBSCR caches)
441441
# Python 3.12b1 3525 (Shrink the CALL caches)
442442
# Python 3.12b1 3526 (Add instrumentation support)
443-
# Python 3.12b1 3527 (Optimize super() calls)
443+
# Python 3.12b1 3527 (Add LOAD_SUPER_ATTR)
444+
# Python 3.12b1 3528 (Add LOAD_SUPER_ATTR_METHOD specialization)
444445

445446
# Python 3.13 will start with 3550
446447

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

460-
MAGIC_NUMBER = (3527).to_bytes(2, 'little') + b'\r\n'
461+
MAGIC_NUMBER = (3528).to_bytes(2, 'little') + b'\r\n'
461462

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

Lib/opcode.py

+9
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ def pseudo_op(name, op, real_ops):
353353
"FOR_ITER_RANGE",
354354
"FOR_ITER_GEN",
355355
],
356+
"LOAD_SUPER_ATTR": [
357+
"LOAD_SUPER_ATTR_METHOD",
358+
],
356359
"LOAD_ATTR": [
357360
# These potentially push [NULL, bound method] onto the stack.
358361
"LOAD_ATTR_CLASS",
@@ -426,6 +429,12 @@ def pseudo_op(name, op, real_ops):
426429
"FOR_ITER": {
427430
"counter": 1,
428431
},
432+
"LOAD_SUPER_ATTR": {
433+
"counter": 1,
434+
"class_version": 2,
435+
"self_type_version": 2,
436+
"method": 4,
437+
},
429438
"LOAD_ATTR": {
430439
"counter": 1,
431440
"version": 2,

Lib/test/test_opcache.py

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
import unittest
22

33

4+
class TestLoadSuperAttrCache(unittest.TestCase):
5+
def test_descriptor_not_double_executed_on_spec_fail(self):
6+
calls = []
7+
class Descriptor:
8+
def __get__(self, instance, owner):
9+
calls.append((instance, owner))
10+
return lambda: 1
11+
12+
class C:
13+
d = Descriptor()
14+
15+
class D(C):
16+
def f(self):
17+
return super().d()
18+
19+
d = D()
20+
21+
self.assertEqual(d.f(), 1) # warmup
22+
calls.clear()
23+
self.assertEqual(d.f(), 1) # try to specialize
24+
self.assertEqual(calls, [(d, D)])
25+
26+
427
class TestLoadAttrCache(unittest.TestCase):
528
def test_descriptor_added_after_optimization(self):
629
class Descriptor:
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
Add :opcode:`LOAD_SUPER_ATTR` to speed up ``super().meth()`` and ``super().attr`` calls.
1+
Add :opcode:`LOAD_SUPER_ATTR` (and a specialization for ``super().method()``) to
2+
speed up ``super().method()`` and ``super().attr``. This makes
3+
``super().method()`` roughly 2.3x faster and brings it within 20% of the
4+
performance of a simple method call. Patch by Vladimir Matveev and Carl Meyer.

0 commit comments

Comments
 (0)