This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: __slots__ make attribute setting 10x slower
Type: resource usage Stage:
Components: Interpreter Core Versions: Python 2.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: amaury.forgeotdarc, christian.heimes, facundobatista, jyasskin, theller
Priority: high Keywords:

Created on 2008-02-14 18:01 by jyasskin, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Messages (10)
msg62408 - (view) Author: Jeffrey Yasskin (jyasskin) * (Python committer) Date: 2008-02-14 18:01
(On a MacBook Pro 2.33 GHz)

$ ./python.exe -m timeit -s 'class Foo(object): pass' -s 'f = Foo()'
'f.num = 3'
10000000 loops, best of 3: 0.13 usec per loop
$ ./python.exe -m timeit -s 'class Foo(object): __slots__ = ["num"]' -s
'f = Foo()' 'f.num = 3'
1000000 loops, best of 3: 1.24 usec per loop

Attribute reading isn't affected:
$ ./python.exe -m timeit -s 'class Foo(object): pass' -s 'f = Foo();
f.num = 3' 'g = f.num'
10000000 loops, best of 3: 0.107 usec per loop
$ ./python.exe -m timeit -s 'class Foo(object): __slots__ = ["num"]' -s
'f = Foo(); f.num = 3' 'g = f.num'
10000000 loops, best of 3: 0.101 usec per loop
msg62409 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2008-02-14 18:50
This slowdown is due to the new implementation of isinstance in python2.6.

If I comment most of the code of PyObject_IsInstance in
Objects/abstract.c (remove all __instancecheck__ stuff; leave only the
last line), timings are much better, and more similar to 2.5 performance.
msg62410 - (view) Author: Thomas Heller (theller) * (Python committer) Date: 2008-02-14 20:09
I think this is a serious problem (and thanks, amaury, for finding the
spot). comtypes, like ctypes, uses quite a bit of isinstance calls.

It is the reason that the comtypes unit tests run between 8% and 25%
slower with trunk than with python 2.5.1.  If I comment out the code
that you mentioned, python trunk is slightly faster than 2.5.1.  I
suggest to raise the severity.
msg62411 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2008-02-14 20:36
I agree, Thomas
msg62412 - (view) Author: Thomas Heller (theller) * (Python committer) Date: 2008-02-14 20:55
PyObject_IsSubclass() has the same problem.

BTW; calling PyObject_GetAttr() with interned strings for
"__instancecheck__" and "__subclasscheck__" brings not enough speedup.
msg62414 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2008-02-14 21:30
Something interesting:
- attribute setting uses PyObject_IsInstance, which is slower since the
introduction of ABCs.
- attribute reading uses PyObject_TypeCheck, which only searches the
__mro__.

Does this means that many calls to IsInstance could be replaced by
TypeCheck? Of course this makes sense only for new-style classes, but it
is the case for all built-in objects.
msg62415 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2008-02-14 21:46
With this trivial patch, all tests still pass:

Index: Objects/descrobject.c
===================================================================
--- Objects/descrobject.c       (revision 60754)
+++ Objects/descrobject.c       (working copy)
@@ -166,7 +166,7 @@
               int *pres)
 {
        assert(obj != NULL);
-       if (!PyObject_IsInstance(obj, (PyObject *)(descr->d_type))) {
+       if (!PyObject_TypeCheck(obj, descr->d_type)) {
                PyErr_Format(PyExc_TypeError,
                             "descriptor '%.200s' for '%.100s' objects "
                             "doesn't apply to '%.100s' object",
msg62417 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2008-02-14 22:42
Thomas Heller wrote:
> BTW; calling PyObject_GetAttr() with interned strings for
> "__instancecheck__" and "__subclasscheck__" brings not enough speedup.

I've implemented the interning as static PyObject* in r60822. It should
give a small speedup.
msg62434 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2008-02-15 18:59
__instancecheck__ is not only slower, it can also cause crashes:

import abc
class MyABC:
    __metaclass__ = abc.ABCMeta
    __slots__ = ["a"]

class Unrelated:
    pass
MyABC.register(Unrelated)

u=Unrelated()
assert isinstance(u, MyABC)

MyABC.a.__set__(u, 3) # Boom


The patch I proposed above correctly raises the error:
TypeError: descriptor 'a' for 'MyABC' objects doesn't apply to
'Unrelated' object
msg62441 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2008-02-15 21:30
I think I fixed the issue.
"svn annotate" showed that the exact same optimization was applied on
__slots__ read access, five years ago: r28297.
History
Date User Action Args
2022-04-11 14:56:30adminsetgithub: 46369
2008-02-15 21:30:40amaury.forgeotdarcsetstatus: open -> closed
resolution: fixed
messages: + msg62441
2008-02-15 18:59:17amaury.forgeotdarcsetmessages: + msg62434
2008-02-14 22:42:31christian.heimessetmessages: + msg62417
2008-02-14 21:46:47amaury.forgeotdarcsetmessages: + msg62415
2008-02-14 21:30:38amaury.forgeotdarcsetmessages: + msg62414
2008-02-14 20:55:48thellersetmessages: + msg62412
2008-02-14 20:36:31christian.heimessetpriority: high
nosy: + christian.heimes
messages: + msg62411
2008-02-14 20:09:45thellersetnosy: + theller
messages: + msg62410
2008-02-14 18:50:05amaury.forgeotdarcsetnosy: + amaury.forgeotdarc
messages: + msg62409
2008-02-14 18:10:43facundobatistasetnosy: + facundobatista
2008-02-14 18:01:36jyasskincreate