New-style classes + __getattr__ sucks, here is why:
# new-style class with deep inheritance with root=object
class Base(...)
class A(Base): class B(Base):
... ...
# no __getattr__ def __getattr__(self, name):
# not important what is here
pass
def func(): def func():
# some function # some function
pass pass
a = A()
b = B()
# note - no call is made, just attribute access
%timeit a.func --> 830 ns
%timeit b.func --> 2.19 µs = 2190 ns
That's more than 2.5x slowdown!
----
Let's have a look here:
http://landau.phys.spbu.ru/~kirr/cgi-bin/hg.cgi/py-fast-property
The following is what I get on my host (rev
a063185b02f5 used):
========================================
*** INSTANCE VAR ACCESS TIMES (ns) ***
========================================
cls cls_o cls(BB) cls(OO) note
E: 527 607 591 589 class var (just for reference -- can *not* be used)
I: 586 485 592 481 __slots__
F: 715 478 1625 488 inst var
G: 691 477 1638 482 inst var (+ another inst vars)
J: 881 477 1825 477 __getattr__ (empty) + __slots__
C: 2035 2155 1966 2160 @property
D: 1017 479 2885 490 __getattr__ (cached)
H: 1010 488 2903 477 __getattr__ (empty) + inst var
d: 7190 7186 9381 8850 __getattr__
=========================================
*** INSTANCE FUNC ACCESS TIMES (ns) ***
=========================================
cls cls_o cls(BB) cls(OO) note
I: 719 757 704 780 __slots__
G: 797 768 771 767 inst var (+ another inst vars)
F: 781 769 775 790 inst var
C: 689 782 778 793 @property
E: 686 828 782 774 class var (just for reference -- can *not* be used)
J: 1011 761 1969 818 __getattr__ (empty) + __slots__
d: 1010 769 2041 770 __getattr__
D: 1079 792 2048 757 __getattr__ (cached)
H: 1089 781 2111 769 __getattr__ (empty) + inst var
----
legend:
cls -- new-style class without base
cls_o -- old-style class without base
cls_BB -- new-style class with long base
cls_OO -- old-style class with long base
For -*- new-style -*- classes this means that:
- table 1 -
(F) access to __dict__ vars is much slower in case of deep inheritance
(I) access to __slots__ vars is independent of inheritance
(J,H) access to both either __dict__ or __slots__ instance vars is _much_ slower
in presence of __getattr__ (!)
- table 2 -
(I,F) access to function attributes is independent of inheritance
(J,d,D,H) access to function attributes is _much_ slower if there is a
__getattr__ (!)
So you see, __getattr__ + new-style classes + inheritance is slow even if
__getattr__ is not used at runtime!
But for -*- old-style -*- classes the tables say that instance var & function
attribute access times do not depend on inheritance and are immutable to
__getattr__ presence.
I just can't believe old-style classes are better!?
(An interested reader is directed to read the following parts of CPython
runtime:
Objects/object.c
Objects/typeobject.c
Objects/classobject.c
in particular:
(old-style classes)
`instance_gettattr`
(new-style classes)
`PyObject_GenericGetAttr`
`slot_tp_getattro`
`slot_tp_getattr_hook
and maybe more ...
)
----------------------------------------
So what this all means for SymPy?
Since Basic is a new-style class, and we use __getattr__
all attribute access is *SLOW*
So, we have to choose to either
1. switch to old-style classes, or
2. remove __getattr__ from Basic
Personally, I'm afraid to go the old-style classes way, because:
- new-style classes have nice features as e.g. __slots__
- new-style classes are better maintained
- e.g. Cython classes are new-style (not sure for 100%)
- ...
So here we choose (2).
The only thing that Basic.__getattr__ was used for is to jump to assumptions
code so things like:
expr.is_positive
work.
There is a nice idea how this could be done without __getattr__ (please read
the patch), so I see no reason not to remove it.
So let's make this story short and just do it -- SymPy is 20% faster now:
3
%timeit %timeit fem_test.py integrate(x sin(x), x)
x.diff x.is_integer (cache:on) (cache:on)
old: 2.18 µs 15 µs 3.72 s 5.18 s
new: 1.01 µs 4.2 µs 3.16 s 4.33 s
speedup: 1.16x 3.57x 18% 20%
Signed-off-by: Kirill Smelkov <kirr@landau.phys.spbu.ru>
Signed-off-by: Ondrej Certik <ondrej@certik.cz>
Signed-off-by: Mateusz Paprocki <mattpap@gmail.com>