#20681: Cythonize the special methods in the categories that handle coercion in
arithmetic
-------------------------------------+-------------------------------------
Reporter: nthiery | Owner:
Type: enhancement | Status: needs_work
Priority: major | Milestone: sage-7.3
Component: categories | Resolution:
Keywords: performance | Merged in:
Authors: Nicolas M. Thiéry | Reviewers: Travis Scrimshaw
Report Upstream: N/A | Work issues:
Branch: | Commit:
u/nthiery/cythonize_arithmetic_special_methods_in_the_categories|
2f83f598d7d08ee2002b749e6ba2f81d48983a25
Dependencies: | Stopgaps:
-------------------------------------+-------------------------------------
Description changed by nthiery:
Old description:
> Recall that the Sage categories provide implementations for Python's
> special methods for the basic arithmetic operations (e.g. `__mul__`,
> `__add__`), that dispatch to the coercion model or call Sage's special
> methods (e.g. `_mul_`, `_add_`) for the internal operations.
>
> Before this ticket, this indirection induces an overhead of roughly
> 500ns:
> {{{
> sage: S = Semigroups().example()
> sage: x = S.an_element()
>
> sage: sage: %timeit [x*x for i in range(1000)]
> 1000 loops, best of 3: 1.21 ms per loop
> sage: sage: %timeit [x.__mul__(x) for i in range(1000)]
> 1000 loops, best of 3: 1.12 ms per loop
> sage: sage: %timeit [x._mul_(x) for i in range(1000)]
> 1000 loops, best of 3: 643 µs per loop
> }}}
> (the average timing don't vary much if we try again)
>
> After this ticket, which Cythonize those methods, the overhead is
> reduced to roughly 260ns, which is a speedup by a factor of 2:
> {{{
> sage: %timeit [x*x for i in range(1000)]
> 1000 loops, best of 3: 929 µs per loop
> sage: %timeit [x.__mul__(x) for i in range(1000)]
> 1000 loops, best of 3: 884 µs per loop
> sage: %timeit [x._mul_(x) for i in range(1000)]
> 1000 loops, best of 3: 624 µs per loop
> }}}
New description:
Recall that the Sage categories provide implementations for Python's
special methods for the basic arithmetic operations (e.g. `__mul__`,
`__add__`), that dispatch to the coercion model or call Sage's special
methods (e.g. `_mul_`, `_add_`) for the internal
operations. Furthermore, the default implementation for `_mul_` is to
delegate the work to the `product` method of the parent.
Before this ticket, those indirections induced an overhead of roughly
100ns for `*`, 480ns for `__mul__` and 340ns for `_mul_`:
{{{
sage: S = Semigroups().example()
sage: x = S.an_element()
sage: x*x
42
sage: sage: %timeit [x*x for i in range(1000)]
1000 loops, best of 3: 1.21 ms per loop
sage: sage: %timeit [x.__mul__(x) for i in range(1000)]
1000 loops, best of 3: 1.12 ms per loop
sage: sage: %timeit [x._mul_(x) for i in range(1000)]
1000 loops, best of 3: 643 µs per loop
sage: %timeit [S.product(x,x) for i in range(1000)]
1000 loops, best of 3: 300 µs per loop
}}}
After this ticket, which Cythonize those methods, the overhead is
reduced to roughly 20ns for `*`, 100ns for `__mul__` and 160ms for
`_mul_`, which is a speedup by a factor of 3 on the accumulated
overhead, and an overall speedup by a factor of 2 for this particular
parent:
{{{
sage: %timeit [x*x for i in range(1000)]
1000 loops, best of 3: 596 µs per loop
sage: %timeit [x.__mul__(x) for i in range(1000)]
1000 loops, best of 3: 572 µs per loop
sage: %timeit [x._mul_(x) for i in range(1000)]
1000 loops, best of 3: 467 µs per loop
sage: %timeit [S.product(x,x) for i in range(1000)]
1000 loops, best of 3: 300 µs per loop
}}}
Notes
=====
The average timings vary by about 5-10% from one run to the other.
In this parent, the product is trivial, and implemented as a plain
Python `product` method in the parent. For a parent with a non trivial
Python product, the overhead of the indirections is now of 20%, which
is getting closer to acceptable::
{{{
sage: S = Semigroups().Finite().example()
sage: x = S.an_element()
sage: %timeit [S.product(x,x) for i in range(1000)]
100 loops, best of 3: 4.39 ms per loop
sage: %timeit [x*x for i in range(1000)]
100 loops, best of 3: 5.28 ms per loop
}}}
For comparison, here is the cost of the product of two (Sage)
integers::
{{{
sage: x = 3
sage: %timeit [x*x for i in range(1000)]
1000 loops, best of 3: 162 µs per loop
sage: x = int(3)
sage: %timeit [x*x for i in range(1000)]
10000 loops, best of 3: 63.9 µs per loop
}}}
So the 120ns overhead when using coercion methods `__mul__` provided
by categories (rather than from a Cython super class) is of the same
order of magnitude as the cost of a trivial multiplication.
--
--
Ticket URL: <http://trac.sagemath.org/ticket/20681#comment:5>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica,
and MATLAB
--
You received this message because you are subscribed to the Google Groups
"sage-trac" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sage-trac.
For more options, visit https://groups.google.com/d/optout.