mirror of https://github.com/python/cpython
bpo-36012: Avoid linear slot search for non-dunder methods (GH-11907)
This commit is contained in:
parent
b5409dacc4
commit
d8b9e1fc2e
|
@ -375,6 +375,11 @@ Optimizations
|
||||||
This makes the created list 12% smaller on average. (Contributed by
|
This makes the created list 12% smaller on average. (Contributed by
|
||||||
Raymond Hettinger and Pablo Galindo in :issue:`33234`.)
|
Raymond Hettinger and Pablo Galindo in :issue:`33234`.)
|
||||||
|
|
||||||
|
* Doubled the speed of class variable writes. When a non-dunder attribute
|
||||||
|
was updated, there was an unnecessary call to update slots.
|
||||||
|
(Contributed by Stefan Behnel, Pablo Galindo Salgado, Raymond Hettinger,
|
||||||
|
Neil Schemenauer, and Serhiy Storchaka in :issue:`36012`.)
|
||||||
|
|
||||||
|
|
||||||
Build and C API Changes
|
Build and C API Changes
|
||||||
=======================
|
=======================
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Doubled the speed of class variable writes. When a non-dunder attribute was
|
||||||
|
updated, there was an unnecessary call to update slots.
|
|
@ -3164,6 +3164,24 @@ _PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name)
|
||||||
return _PyType_Lookup(type, oname);
|
return _PyType_Lookup(type, oname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if the "readied" PyUnicode name
|
||||||
|
is a double-underscore special name. */
|
||||||
|
static int
|
||||||
|
is_dunder_name(PyObject *name)
|
||||||
|
{
|
||||||
|
Py_ssize_t length = PyUnicode_GET_LENGTH(name);
|
||||||
|
int kind = PyUnicode_KIND(name);
|
||||||
|
/* Special names contain at least "__x__" and are always ASCII. */
|
||||||
|
if (length > 4 && kind == PyUnicode_1BYTE_KIND) {
|
||||||
|
Py_UCS1 *characters = PyUnicode_1BYTE_DATA(name);
|
||||||
|
return (
|
||||||
|
((characters[length-2] == '_') && (characters[length-1] == '_')) &&
|
||||||
|
((characters[0] == '_') && (characters[1] == '_'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* This is similar to PyObject_GenericGetAttr(),
|
/* This is similar to PyObject_GenericGetAttr(),
|
||||||
but uses _PyType_Lookup() instead of just looking in type->tp_dict. */
|
but uses _PyType_Lookup() instead of just looking in type->tp_dict. */
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -3275,12 +3293,14 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
PyUnicode_InternInPlace(&name);
|
|
||||||
if (!PyUnicode_CHECK_INTERNED(name)) {
|
if (!PyUnicode_CHECK_INTERNED(name)) {
|
||||||
PyErr_SetString(PyExc_MemoryError,
|
PyUnicode_InternInPlace(&name);
|
||||||
"Out of memory interning an attribute name");
|
if (!PyUnicode_CHECK_INTERNED(name)) {
|
||||||
Py_DECREF(name);
|
PyErr_SetString(PyExc_MemoryError,
|
||||||
return -1;
|
"Out of memory interning an attribute name");
|
||||||
|
Py_DECREF(name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -3289,7 +3309,16 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
|
||||||
}
|
}
|
||||||
res = _PyObject_GenericSetAttrWithDict((PyObject *)type, name, value, NULL);
|
res = _PyObject_GenericSetAttrWithDict((PyObject *)type, name, value, NULL);
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
res = update_slot(type, name);
|
/* Clear the VALID_VERSION flag of 'type' and all its
|
||||||
|
subclasses. This could possibly be unified with the
|
||||||
|
update_subclasses() recursion in update_slot(), but carefully:
|
||||||
|
they each have their own conditions on which to stop
|
||||||
|
recursing into subclasses. */
|
||||||
|
PyType_Modified(type);
|
||||||
|
|
||||||
|
if (is_dunder_name(name)) {
|
||||||
|
res = update_slot(type, name);
|
||||||
|
}
|
||||||
assert(_PyType_CheckConsistency(type));
|
assert(_PyType_CheckConsistency(type));
|
||||||
}
|
}
|
||||||
Py_DECREF(name);
|
Py_DECREF(name);
|
||||||
|
@ -7236,13 +7265,6 @@ update_slot(PyTypeObject *type, PyObject *name)
|
||||||
assert(PyUnicode_CheckExact(name));
|
assert(PyUnicode_CheckExact(name));
|
||||||
assert(PyUnicode_CHECK_INTERNED(name));
|
assert(PyUnicode_CHECK_INTERNED(name));
|
||||||
|
|
||||||
/* Clear the VALID_VERSION flag of 'type' and all its
|
|
||||||
subclasses. This could possibly be unified with the
|
|
||||||
update_subclasses() recursion below, but carefully:
|
|
||||||
they each have their own conditions on which to stop
|
|
||||||
recursing into subclasses. */
|
|
||||||
PyType_Modified(type);
|
|
||||||
|
|
||||||
init_slotdefs();
|
init_slotdefs();
|
||||||
pp = ptrs;
|
pp = ptrs;
|
||||||
for (p = slotdefs; p->name; p++) {
|
for (p = slotdefs; p->name; p++) {
|
||||||
|
@ -7281,6 +7303,9 @@ update_all_slots(PyTypeObject* type)
|
||||||
{
|
{
|
||||||
slotdef *p;
|
slotdef *p;
|
||||||
|
|
||||||
|
/* Clear the VALID_VERSION flag of 'type' and all its subclasses. */
|
||||||
|
PyType_Modified(type);
|
||||||
|
|
||||||
init_slotdefs();
|
init_slotdefs();
|
||||||
for (p = slotdefs; p->name; p++) {
|
for (p = slotdefs; p->name; p++) {
|
||||||
/* update_slot returns int but can't actually fail */
|
/* update_slot returns int but can't actually fail */
|
||||||
|
|
Loading…
Reference in New Issue