gh-87729: add LOAD_SUPER_ATTR instruction for faster super() (#103497)

This speeds up `super()` (by around 85%, for a simple one-level
`super().meth()` microbenchmark) by avoiding allocation of a new
single-use `super()` object on each use.
This commit is contained in:
Carl Meyer 2023-04-24 16:22:14 -06:00 committed by GitHub
parent 22bed58e53
commit 0dc8b50d33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 783 additions and 408 deletions

View file

@ -9346,26 +9346,20 @@ super_repr(PyObject *self)
su->type ? su->type->tp_name : "NULL");
}
// if `method` is non-NULL, we are looking for a method descriptor,
// and setting `*method` to 1 means we found one.
static PyObject *
super_getattro(PyObject *self, PyObject *name)
do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
PyTypeObject *su_obj_type, PyObject *name, int *method)
{
superobject *su = (superobject *)self;
PyTypeObject *starttype;
PyObject *mro;
PyObject *mro, *res;
Py_ssize_t i, n;
int temp_su = 0;
starttype = su->obj_type;
if (starttype == NULL)
if (su_obj_type == NULL)
goto skip;
/* We want __class__ to return the class of the super object
(i.e. super, or a subclass), not the class of su->obj. */
if (PyUnicode_Check(name) &&
PyUnicode_GET_LENGTH(name) == 9 &&
_PyUnicode_Equal(name, &_Py_ID(__class__)))
goto skip;
mro = starttype->tp_mro;
mro = su_obj_type->tp_mro;
if (mro == NULL)
goto skip;
@ -9374,14 +9368,14 @@ super_getattro(PyObject *self, PyObject *name)
/* No need to check the last one: it's gonna be skipped anyway. */
for (i = 0; i+1 < n; i++) {
if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i))
if ((PyObject *)(su_type) == PyTuple_GET_ITEM(mro, i))
break;
}
i++; /* skip su->type (if any) */
if (i >= n)
goto skip;
/* keep a strong reference to mro because starttype->tp_mro can be
/* keep a strong reference to mro because su_obj_type->tp_mro can be
replaced during PyDict_GetItemWithError(dict, name) */
Py_INCREF(mro);
do {
@ -9389,19 +9383,23 @@ super_getattro(PyObject *self, PyObject *name)
PyObject *dict = _PyType_CAST(obj)->tp_dict;
assert(dict != NULL && PyDict_Check(dict));
PyObject *res = PyDict_GetItemWithError(dict, name);
res = PyDict_GetItemWithError(dict, name);
if (res != NULL) {
Py_INCREF(res);
descrgetfunc f = Py_TYPE(res)->tp_descr_get;
if (f != NULL) {
PyObject *res2;
res2 = f(res,
/* Only pass 'obj' param if this is instance-mode super
(See SF ID #743627) */
(su->obj == (PyObject *)starttype) ? NULL : su->obj,
(PyObject *)starttype);
Py_SETREF(res, res2);
if (method && _PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
*method = 1;
}
else {
descrgetfunc f = Py_TYPE(res)->tp_descr_get;
if (f != NULL) {
PyObject *res2;
res2 = f(res,
/* Only pass 'obj' param if this is instance-mode super
(See SF ID #743627) */
(su_obj == (PyObject *)su_obj_type) ? NULL : su_obj,
(PyObject *)su_obj_type);
Py_SETREF(res, res2);
}
}
Py_DECREF(mro);
@ -9417,7 +9415,34 @@ super_getattro(PyObject *self, PyObject *name)
Py_DECREF(mro);
skip:
return PyObject_GenericGetAttr(self, name);
if (su == NULL) {
PyObject *args[] = {(PyObject *)su_type, su_obj};
su = (superobject *)PyObject_Vectorcall((PyObject *)&PySuper_Type, args, 2, NULL);
if (su == NULL) {
return NULL;
}
temp_su = 1;
}
res = PyObject_GenericGetAttr((PyObject *)su, name);
if (temp_su) {
Py_DECREF(su);
}
return res;
}
static PyObject *
super_getattro(PyObject *self, PyObject *name)
{
superobject *su = (superobject *)self;
/* We want __class__ to return the class of the super object
(i.e. super, or a subclass), not the class of su->obj. */
if (PyUnicode_Check(name) &&
PyUnicode_GET_LENGTH(name) == 9 &&
_PyUnicode_Equal(name, &_Py_ID(__class__)))
return PyObject_GenericGetAttr(self, name);
return do_super_lookup(su, su->type, su->obj, su->obj_type, name, NULL);
}
static PyTypeObject *
@ -9473,6 +9498,18 @@ supercheck(PyTypeObject *type, PyObject *obj)
return NULL;
}
PyObject *
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *method)
{
PyTypeObject *su_obj_type = supercheck(su_type, su_obj);
if (su_obj_type == NULL) {
return NULL;
}
PyObject *res = do_super_lookup(NULL, su_type, su_obj, su_obj_type, name, method);
Py_DECREF(su_obj_type);
return res;
}
static PyObject *
super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{