8000 bpo-36012: Avoid linear slot search for non-dunder methods (GH-11907) · python/cpython@d8b9e1f · GitHub
[go: up one dir, main page]

Skip to content

Commit d8b9e1f

Browse files
scoderrhettinger
authored andcommitted
bpo-36012: Avoid linear slot search for non-dunder methods (GH-11907)
1 parent b5409da commit d8b9e1f

File tree

3 files changed

+45
-13
lines changed

3 files changed

+45
-13
lines changed

Doc/whatsnew/3.8.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,11 @@ Optimizations
375375
This makes the created list 12% smaller on average. (Contributed by
376376
Raymond Hettinger and Pablo Galindo in :issue:`33234`.)
377377

378+
* Doubled the speed of class variable writes. When a non-dunder attribute
379+
was updated, there was an unnecessary call to update slots.
380+
(Contributed by Stefan Behnel, Pablo Galindo Salgado, Raymond Hettinger,
381+
Neil Schemenauer, and Serhiy Storchaka in :issue:`36012`.)
382+
378383

379384
Build and C API Changes
380385
=======================
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Doubled the speed of class variable writes. When a non-dunder attribute was
2+
updated, there was an unnecessary call to update slots.

Objects/typeobject.c

Lines changed: 38 additions & 13 deletions
< 10000 /div>
Original file line numberDiff line numberDiff line change
@@ -3164,6 +3164,24 @@ _PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name)
31643164
return _PyType_Lookup(type, oname);
31653165
}
31663166

3167+
/* Check if the "readied" PyUnicode name
3168+
is a double-underscore special name. */
3169+
static int
3170+
is_dunder_name(PyObject *name)
3171+
{
3172+
Py_ssize_t length = PyUnicode_GET_LENGTH(name);
3173+
int kind = PyUnicode_KIND(name);
3174+
/* Special names contain at least "__x__" and are always ASCII. */
3175+
if (length > 4 && kind == PyUnicode_1BYTE_KIND) {
3176+
Py_UCS1 *characters = PyUnicode_1BYTE_DATA(name);
3177+
return (
3178+
((characters[length-2] == '_') && (characters[length-1] == '_')) &&
3179+
((characters[0] == '_') && (characters[1] == '_'))
3180+
);
3181+
}
3182+
return 0;
3183+
}
3184+
31673185
/* This is similar to PyObject_GenericGetAttr(),
31683186
but uses _PyType_Lookup() instead of just looking in type->tp_dict. */
31693187
static PyObject *
@@ -3275,12 +3293,14 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
32753293
if (name == NULL)
32763294
return -1;
32773295
}
3278-
PyUnicode_InternInPlace(&name);
32793296
if (!PyUnicode_CHECK_INTERNED(name)) {
3280-
PyErr_SetString(PyExc_MemoryError,
3281-
"Out of memory interning an attribute name");
3282-
Py_DECREF(name);
3283-
return -1;
3297+
PyUnicode_InternInPlace(&name);
3298+
if (!PyUnicode_CHECK_INTERNED(name)) {
3299+
PyErr_SetString(PyExc_MemoryError,
3300+
"Out of memory interning an attribute name");
3301+
Py_DECREF(name);
3302+
return -1;
3303+
}
32843304
}
32853305
}
32863306
else {
@@ -3289,7 +3309,16 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
32893309
}
32903310
res = _PyObject_GenericSetAttrWithDict((PyObject *)type, name, value, NULL);
32913311
if (res == 0) {
3292-
res = update_slot(type, name);
3312+
/* Clear the VALID_VERSION flag of 'type' and all its
3313+
subclasses. This could possibly be unified with the
3314+
update_subclasses() recursion in update_slot(), but carefully:
3315+
they each have their own conditions on which to stop
3316+
recursing into subclasses. */
3317+
PyType_Modified(type);
3318+
3319+
if (is_dunder_name(name)) {
3320+
res = update_slot(type, name);
3321+
}
32933322
assert(_PyType_CheckConsistency(type));
32943323
}
32953324
Py_DECREF(name);
@@ -7236,13 +7265,6 @@ update_slot(PyTypeObject *type, PyObject *name)
72367265
assert(PyUnicode_CheckExact(name));
72377266
assert(PyUnicode_CHECK_INTERNED(name));
72387267

7239-
/* Clear the VALID_VERSION flag of 'type' and all its
7240-
subclasses. This could possibly be unified with the
7241-
update_subclasses() recursion below, but carefully:
7242-
they each have their own conditions on which to stop
7243-
recursing into subclasses. */
7244-
PyType_Modified(type);
7245-
72467268
init_slotdefs();
72477269
pp = ptrs;
72487270
for (p = slotdefs; p->name; p++) {
@@ -7281,6 +7303,9 @@ update_all_slots(PyTypeObject* type)
72817303
{
72827304
slotdef *p;
72837305

7306+
/* Clear the VALID_VERSION flag of 'type' and all its subclasses. */
7307+
PyType_Modified(type);
7308+
72847309
init_slotdefs();
72857310
for (p = slotdefs; p->name; p++) {
72867311
/* update_slot returns int but can't actually fail */

0 commit comments

Comments
 (0)
0