bpo-36012: Avoid linear slot search for non-dunder methods (GH-11907)

This commit is contained in:
Stefan Behnel 2019-02-20 18:29:24 +01:00 committed by Raymond Hettinger
parent b5409dacc4
commit d8b9e1fc2e
3 changed files with 45 additions and 13 deletions

View File

@ -375,6 +375,11 @@ Optimizations
This makes the created list 12% smaller on average. (Contributed by
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
=======================

View File

@ -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.

View File

@ -3164,6 +3164,24 @@ _PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name)
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(),
but uses _PyType_Lookup() instead of just looking in type->tp_dict. */
static PyObject *
@ -3275,6 +3293,7 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
if (name == NULL)
return -1;
}
if (!PyUnicode_CHECK_INTERNED(name)) {
PyUnicode_InternInPlace(&name);
if (!PyUnicode_CHECK_INTERNED(name)) {
PyErr_SetString(PyExc_MemoryError,
@ -3283,13 +3302,23 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
return -1;
}
}
}
else {
/* Will fail in _PyObject_GenericSetAttrWithDict. */
Py_INCREF(name);
}
res = _PyObject_GenericSetAttrWithDict((PyObject *)type, name, value, NULL);
if (res == 0) {
/* 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));
}
Py_DECREF(name);
@ -7236,13 +7265,6 @@ update_slot(PyTypeObject *type, PyObject *name)
assert(PyUnicode_CheckExact(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();
pp = ptrs;
for (p = slotdefs; p->name; p++) {
@ -7281,6 +7303,9 @@ update_all_slots(PyTypeObject* type)
{
slotdef *p;
/* Clear the VALID_VERSION flag of 'type' and all its subclasses. */
PyType_Modified(type);
init_slotdefs();
for (p = slotdefs; p->name; p++) {
/* update_slot returns int but can't actually fail */