Performance difference for Binding=True

19 views
Skip to first unread message

Nils Bruin

unread,
Nov 20, 2022, 2:04:31 PM11/20/22
to cython-users
Dear Cython developers,

We are investigating what would be the consequences in Sagemath for moving to default "Binding=True".  For instance, in


we're finding the following with

-------------
%%cython
cimport cython
@cython.binding(False)
cdef class Unbinding:
    def test(self):
        return 1
@cython.binding(True)
cdef class Binding:
    def test(self):
        return 1

def fetch_test(a, N=100):
    for i in range(N):
        _ = a.test

def call_test(a, N=100):
    for i in range(N):
        _ = a.test()
-------------

sage: U=Unbinding()
....: B=Binding()
sage: %timeit fetch_test(U,100000)
2.95 ms ± 37.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
sage: %timeit fetch_test(B,100000)
2.67 ms ± 31.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
sage: %timeit call_test(U,100000)
3.52 ms ± 44.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
sage: %timeit call_test(B,100000)
4.71 ms ± 99 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-------------

so it seems fetching a method has become slightly cheaper (that was a surprise!) but calling it seems to be quite a bit more expensive. In fact, an unfortunate coding style in Sagemath of providing access to attributes mainly through accessor functions gets hit by this rather badly: a method like "basering" increases in time by something like 25%.

Is there any chance that on the cython side this overhead could be reduced? Presently we're faced with several options:
 1) keep the default `Binding=False` in sage when we transition to Cython 3
 2) somehow profile and determine particularly cheap functions and decorate those with `Binding=False`
3) just go with `Binding=True` everywhere

Each has significant drawbacks:
 1) would place us in a rather non-standard use of cython (plus getting Binding=True has some call stack advantages -- probably why it's more expensive)
 2) would be a lot of work and would make debugging sage more difficult, because different cython routines would have different call stack structures.
3) runs the risk of deteriorating performance measurably for certain programs, just by upgrading to Cython 3

It would be nice to be able to go with 3), hence the inquiry into whether its costs could be reduced. Any insights? Did we profile in a way that gives us skewed results (as far as we can see the effect is real and measurable on some doctests that weren't particularly written to challenge call cost, so it seems to be measurable in the real world).

Kind regards,

Nils Bruin

D Woods

unread,
Nov 22, 2022, 1:16:55 PM11/22/22
to cython-users
Thanks for reporting this - I've created a github issue to track it https://github.com/cython/cython/issues/5144. We can definitely have a look, although you should probably plan on the basis that it won't be fixed (i.e. until we've had a proper look, I don't think we know if it's even fixable!).

Do you have any information about what Python version this was measured on? I imagine something fairly recent?
Reply all
Reply to author
Forward
0 new messages