ionelmc / python-lazy-object-proxy

A fast and thorough lazy object proxy.
BSD 2-Clause "Simplified" License
247 stars 36 forks source link

Speed up arithmetic operations involving Proxy subclasses #18

Closed embray closed 7 years ago

embray commented 7 years ago

Arithmetic operations on subclasses of Proxy (specifically the C implementation) are slower (roughly three times) due to inexact match in the PyObject_Instance call in Proxy__WRAPPED_REPLACE_OR_RETURN_NULL. For simple C-level type compatibility checking PyObject_TypeCheck is much faster. In fact the PyObject_IsInstance is probably superfluous with this but I left it anyways.

Before:

In [1]: from lazy_object_proxy import Proxy

In [2]: a = Proxy(int)

In [3]: str(a)
Out[3]: '0'

In [4]: %timeit -n 1000000 -r 100 a + a
1000000 loops, best of 100: 61.1 ns per loop

In [5]: class MyProxy(Proxy): pass

In [6]: b = MyProxy(int)

In [7]: str(b)
Out[7]: '0'

In [8]: %timeit -n 1000000 -r 100 b + b
1000000 loops, best of 100: 171 ns per loop

After:

In [1]: from lazy_object_proxy import Proxy

In [2]: a = Proxy(int)

In [3]: str(a)
Out[3]: '0'

In [4]: %timeit -n 1000000 -r 100 a + a
1000000 loops, best of 100: 57.4 ns per loop

In [5]: class MyProxy(Proxy): pass

In [6]: b = MyProxy(int)

In [7]: str(b)
Out[7]: '0'

In [8]: %timeit -n 1000000 -r 100 b + b
1000000 loops, best of 100: 62.1 ns per loop
ionelmc commented 7 years ago

Good catch! Now I'm thinking we don't need to do a full isinstance check (we don't care about things like https://docs.python.org/3/reference/datamodel.html?highlight=__instancecheck__#class.__instancecheck__) since a "fake instance" won't have the right memory layout anyway ...

embray commented 7 years ago

I was thinking that too over lunch (it was a very lonely lunch :)

embray commented 7 years ago

Would you like me to update it to just remove the full PyObject_IsInstance?

ionelmc commented 7 years ago

Yes, lets try it.

ionelmc commented 7 years ago

Also, update CHANGELOG/AUTHORS.rst.

embray commented 7 years ago

Done--I agree it makes sense to submit the change upstream to wrapt as well.

codecov-io commented 7 years ago

Codecov Report

Merging #18 into master will decrease coverage by 0.59%. The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##           master      #18     +/-   ##
=========================================
- Coverage   93.81%   93.22%   -0.6%     
=========================================
  Files           8        7      -1     
  Lines        2038     1594    -444     
  Branches      245      245             
=========================================
- Hits         1912     1486    -426     
+ Misses        101       83     -18     
  Partials       25       25
Impacted Files Coverage Δ
src/lazy_object_proxy/__init__.py 85.71% <0%> (-14.29%) :arrow_down:
tests/test_lazy_object_proxy.py 92.11% <0%> (-0.44%) :arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 7033131...cb8ad7b. Read the comment docs.

ionelmc commented 7 years ago

Allright, thanks!

embray commented 7 years ago

Thanks to you too!

ionelmc commented 7 years ago

Published 1.3.0 on PyPI.