8000 Merge remote-tracking branch 'upstream/main' into tvobject · python/cpython@7a8fc14 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7a8fc14

Browse files
committed
Merge remote-tracking branch 'upstream/main' into tvobject
2 parents 02e0a8a + ef25feb commit 7a8fc14

File tree

15 files changed

+537
-345
lines changed

15 files changed

+537
-345
lines changed

Doc/library/types.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,13 @@ Standard names are defined for the following types:
351351
.. versionchanged:: 3.9.2
352352
This type can now be subclassed.
353353

354+
.. seealso::
355+
356+
:ref:`Generic Alias Types<types-genericalias>`
357+
In-depth documentation on instances of :class:`!types.GenericAlias`
358+
359+
:pep:`585` - Type Hinting Generics In Standard Collections
360+
Introducing the :class:`!types.GenericAlias` class
354361

355362
.. class:: UnionType
356363

Include/internal/pycore_code.h

Lines changed: 11 additions & 0 deletions
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

Lines changed: 8 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_typeobject.h

Lines changed: 2 additions & 0 deletions
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

Lines changed: 27 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

Lines changed: 3 additions & 2 deletions
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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@ def pseudo_op(name, op, real_ops):
356356
"FOR_ITER_RANGE",
357357
"FOR_ITER_GEN",
358358
],
359+
"LOAD_SUPER_ATTR": [
360+
"LOAD_SUPER_ATTR_METHOD",
361+
],
359362
"LOAD_ATTR": [
360363
# These potentially push [NULL, bound method] onto the stack.
361364
"LOAD_ATTR_CLASS",
@@ -429,6 +432,12 @@ def pseudo_op(name, op, real_ops):
429432
"FOR_ITER": {
430433
"counter": 1,
431434
},
435+
"LOAD_SUPER_ATTR": {
436+
"counter": 1,
437+
"class_version": 2,
438+
"self_type_version": 2,
439+
"method": 4,
440+
},
432441
"LOAD_ATTR": {
433442
"counter": 1,
434443
"version": 2,

Lib/test/test_opcache.py

Lines changed: 23 additions & 0 deletions
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:
Lines changed: 4 additions & 1 deletion
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.

Objects/typeobject.c

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9393,22 +9393,19 @@ super_repr(PyObject *self)
93939393
su->type ? su->type->tp_name : "NULL");
93949394
}
93959395

9396-
// if `method` is non-NULL, we are looking for a method descriptor,
9397-
// and setting `*method` to 1 means we found one.
9396+
/* Do a super lookup without executing descriptors or falling back to getattr
9397+
on the super object itself.
9398+
9399+
May return NULL with or without an exception set, like PyDict_GetItemWithError. */
93989400
static PyObject *
9399-
do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
9400-
PyTypeObject *su_obj_type, PyObject *name, int *method)
9401+
_super_lookup_descr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject *name)
94019402
{
94029403
PyObject *mro, *res;
94039404
Py_ssize_t i, n;
9404-
int temp_su = 0;
9405-
9406-
if (su_obj_type == NULL)
9407-
goto skip;
94089405

94099406
mro = su_obj_type->tp_mro;
94109407
if (mro == NULL)
9411-
goto skip;
9408+
return NULL;
94129409

94139410
assert(PyTuple_Check(mro));
94149411
n = PyTuple_GET_SIZE(mro);
@@ -9420,7 +9417,7 @@ do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
94209417
}
94219418
i++; /* skip su->type (if any) */
94229419
if (i >= n)
9423-
goto skip;
9420+
return NULL;
94249421

94259422
/* keep a strong reference to mro because su_obj_type->tp_mro can be
94269423
replaced during PyDict_GetItemWithError(dict, name) */
@@ -9433,22 +9430,6 @@ do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
94339430
res = PyDict_GetItemWithError(dict, name);
94349431
if (res != NULL) {
94359432
Py_INCREF(res);
9436-
if (method && _PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
9437-
*method = 1;
9438-
}
9439-
else {
9440-
descrgetfunc f = Py_TYPE(res)->tp_descr_get;
9441-
if (f != NULL) {
9442-
PyObject *res2;
9443-
res2 = f(res,
9444-
/* Only pass 'obj' param if this is instance-mode super
9445-
(See SF ID #743627) */
9446-
(su_obj == (PyObject *)su_obj_type) ? NULL : su_obj,
9447-
(PyObject *)su_obj_type);
9448-
Py_SETREF(res, res2);
9449-
}
9450-
}
9451-
94529433
Py_DECREF(mro);
94539434
return res;
94549435
}
@@ -9460,6 +9441,45 @@ do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
94609441
i++;
94619442
} while (i < n);
94629443
Py_DECREF(mro);
9444+
return NULL;
9445+
}
9446+
9447+
// if `method` is non-NULL, we are looking for a method descriptor,
9448+
// and setting `*method = 1` means we found one.
9449+
static PyObject *
9450+
do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
9451+
PyTypeObject *su_obj_type, PyObject *name, int *method)
9452+
{
9453+
PyObject *res;
9454+
int temp_su = 0;
9455+
9456+
if (su_obj_type == NULL) {
9457+
goto skip;
9458+
}
9459+
9460+
res = _super_lookup_descr(su_type, su_obj_type, name);
9461+
if (res != NULL) {
9462+
if (method && _PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
9463+
*method = 1;
9464+
}
9465+
else {
9466+
descrgetfunc f = Py_TYPE(res)->tp_descr_get;
9467+
if (f != NULL) {
9468+
PyObject *res2;
9469+
res2 = f(res,
9470+
/* Only pass 'obj' param if this is instance-mode super
9471+
(See SF ID #743627) */
9472+
(su_obj == (PyObject *)su_obj_type) ? NULL : su_obj,
9473+
(PyObject *)su_obj_type);
9474+
Py_SETREF(res, res2);
9475+
}
9476+
}
9477+
9478+
return res;
9479+
}
9480+
else if (PyErr_Occurred()) {
9481+
return NULL;
9482+
}
94639483

94649484
skip:
94659485
if (su == NULL) {
@@ -9557,6 +9577,18 @@ _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *me
95579577
return res;
95589578
}
95599579

9580+
PyObject *
9581+
_PySuper_LookupDescr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name)
9582+
{
9583+
PyTypeObject *su_obj_type = supercheck(su_type, su_obj);
9584+
if (su_obj_type == NULL) {
9585+
return NULL;
9586+
}
9587+
PyObject *res = _super_lookup_descr(su_type, su_obj_type, name);
9588+
Py_DECREF(su_obj_type);
9589+
return res;
9590+
}
9591+
95609592
static PyObject *
95619593
super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
95629594
{

0 commit comments

Comments
 (0)
0