#13400: Investigate ways to gain speed in basic constructions
-------------------------------+--------------------------------------------
Reporter: nbruin | Owner: robertwb
Type: enhancement | Status: new
Priority: major | Milestone: sage-wishlist
Component: coercion | Resolution:
Keywords: | Work issues:
Report Upstream: N/A | Reviewers:
Authors: | Merged in:
Dependencies: | Stopgaps:
-------------------------------+--------------------------------------------
Comment (by nbruin):
This example seems fairly typical:
{{{
def test1(N,B,t):
for i in range(t):
m=matrix(QQ,N,N,srange(N*N)).echelon_form()
%prun test1(20,40,10^3)
}}}
In 5.3b2:
{{{
677732 function calls (645652 primitive calls) in 3.976 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1000 2.021 0.002 3.537 0.004 {method 'echelon_form' of
'sage.matrix.matrix_rational_dense.Matrix_rational_dense' objects}
5978 0.236 0.000 0.237 0.000
dynamic_class.py:259(dynamic_class_internal)
5000 0.178 0.000 0.834 0.000 matrix_space.py:1180(matrix)
16384 0.091 0.000 0.099 0.000 {method 'is_prime' of
'sage.rings.integer.Integer' objects}
1991 0.090 0.000 0.103 0.000 {method 'ideal' of
'sage.rings.ring.Ring' objects}
16937/4983 0.082 0.000 0.547 0.000
lazy_attribute.py:506(__get__)
1000 0.076 0.000 0.076 0.000 {method 'range' of
'sage.rings.integer_ring.IntegerRing_class' objects}
1991 0.062 0.000 0.124 0.000
quotient_ring.py:322(__init__)
14394 0.062 0.000 0.192 0.000 arith.py:401(is_prime)
1 0.060 0.060 3.978 3.978 <ipython console>:1(test1)
10000 0.052 0.000 0.235 0.000
matrix_space.py:154(__classcall__)
110854 0.049 0.000 0.049 0.000 {isinstance}
1992 0.049 0.000 0.122 0.000 {sage.rings.ring.is_Field}
2000 0.041 0.000 0.168 0.000
matrix_space.py:230(__init__)
1991 0.039 0.000 0.298 0.000
integer_mod_ring.py:191(__init__)
5977/996 0.038 0.000 0.477 0.000
category.py:1011(parent_class)
2000 0.031 0.000 0.049 0.000 sequence.py:86(Sequence)
2000 0.031 0.000 0.203 0.000
arith.py:1051(previous_prime)
2989 0.031 0.000 0.056 0.000
sageinspect.py:917(sage_getargspec)
5975 0.031 0.000 0.040 0.000
category_types.py:261(__init__)
12000 0.028 0.000 0.056 0.000 misc.py:179(cputime)
2 0.027 0.014 0.031 0.015 __init__.py:2(<module>)
999 0.027 0.000 0.117 0.000
matrix_space.py:1133(zero_matrix)
14394 0.025 0.000 0.030 0.000 all.py:1(arithmetic)
2000 0.020 0.000 0.036 0.000
matrix_space.py:904(_get_matrix_class)
12000 0.020 0.000 0.020 0.000 {resource.getrusage}
8972/6981 0.018 0.000 0.212 0.000
{sage.misc.classcall_metaclass.typecall}
1000 0.017 0.000 0.238 0.000 constructor.py:34(matrix)
1000 0.017 0.000 0.143 0.000 misc.py:1051(srange)
8972/6981 0.017 0.000 0.226 0.000
unique_representation.py:449(__classcall__)
3000 0.016 0.000 0.019 0.000 {method '__copy__' of
'sage.matrix.matrix_integer_dense.Matrix_integer_dense' objects}
997 0.016 0.000 0.049 0.000 homset.py:296(__init__)
1991 0.015 0.000 0.048 0.000
magmas.py:101(__init_extra__)
996 0.015 0.000 0.085 0.000
integer_mod_ring.py:960(_coerce_map_from_)
997 0.015 0.000 0.065 0.000 homset.py:40(Hom)
5972 0.014 0.000 0.019 0.000 category.py:1286(join)
...
}}}
In 5.3b2+tickets
{{{
2262393 function calls (1685916 primitive calls) in 5.520 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1000 2.137 0.002 5.110 0.005 {method 'echelon_form' of
'sage.matrix.matrix_rational_dense.Matrix_rational_dense' objects}
5000 0.263 0.000 0.627 0.000
matrix_space.py:230(__init__)
2224 0.262 0.000 0.351 0.000
quotient_ring.py:322(__init__)
6159 0.194 0.000 0.195 0.000
dynamic_class.py:259(dynamic_class_internal)
5000 0.189 0.000 1.045 0.000 matrix_space.py:1180(matrix)
146080/29216 0.185 0.000 0.497 0.000
covariant_functorial_construction.py:359(is_subcategory)
379808/87648 0.131 0.000 0.462 0.000
covariant_functorial_construction.py:365(<genexpr>)
2224 0.120 0.000 0.139 0.000 {method 'ideal' of
'sage.rings.ring.Ring' objects}
16316 0.093 0.000 0.101 0.000 {method 'is_prime' of
'sage.rings.integer.Integer' objects}
6432 0.091 0.000 0.759 0.000 category.py:112(_join)
1000 0.087 0.000 0.087 0.000 {method 'range' of
'sage.rings.integer_ring.IntegerRing_class' objects}
18217/6220 0.079 0.000 0.699 0.000
lazy_attribute.py:506(__get__)
156240 0.075 0.000 0.170 0.000
category.py:1102(is_subcategory)
1 0.071 0.071 5.522 5.522 <ipython console>:1(test1)
167120/33664 0.066 0.000 0.544 0.000 {any}
14332 0.065 0.000 0.196 0.000 arith.py:401(is_prime)
154016 0.062 0.000 0.095 0.000
category.py:622(_subcategory_hook_)
44296 0.059 0.000 0.074 0.000 weakref.py:55(__getitem__)
129031 0.058 0.000 0.058 0.000 {isinstance}
10000 0.055 0.000 0.751 0.000
matrix_space.py:154(__classcall__)
2983 0.052 0.000 0.467 0.000 {sage.rings.ring.is_Field}
2224 0.047 0.000 0.983 0.000
integer_mod_ring.py:191(__init__)
2000 0.046 0.000 0.222 0.000
arith.py:1051(previous_prime)
1999 0.043 0.000 0.157 0.000
matrix_space.py:1133(zero_matrix)
5000 0.037 0.000 0.064 0.000
matrix_space.py:904(_get_matrix_class)
7000 0.037 0.000 0.048 0.000
category_types.py:261(__init__)
6000/1000 0.037 0.000 0.613 0.001
category.py:1023(parent_class)
19591 0.036 0.000 0.080 0.000 weakref.py:79(__setitem__)
3983 0.035 0.000 0.068 0.000
sageinspect.py:917(sage_getargspec)
154016 0.033 0.000 0.033 0.000 {issubclass}
2000 0.032 0.000 0.052 0.000 sequence.py:86(Sequence)
...
}}}
You can see where a lot of that time is going: In category construction
stuff.
The reason is simple: It's trying some multimodular approach. So there are
all
kinds of matrix spaces over various finite fields involved. Previously,
these
structures remained in memory, cached and ready for reuse. Ideal here, but
given
the peculiar choices of primes, rarely all that useful in practice.
With the stricter collection, these spaces need to be created time and
again.
However, the number of times that covariant_functorial_construction gets
called
is ''insane''. That almost looks like something needs to be constructed
for
''every element''.
On the plus side, if we run instead
{{{
def test1(N,B,t):
for i in range(t):
m=matrix(QQ,N,N,srange(N*N)).echelon_form(algorithm="classical")
%prun test1(20,40,10^3)
}}}
we get with 5.3b2
{{{
95507 function calls (95450 primitive calls) in 0.744 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1000 0.323 0.000 0.333 0.000 {method 'echelon_form' of
'sage.matrix.matrix_rational_dense.Matrix_rational_dense' objects}
1000 0.192 0.000 0.195 0.000 matrix_space.py:1180(matrix)
1 0.046 0.046 0.745 0.745 <ipython console>:1(test1)
2000 0.029 0.000 0.045 0.000 sequence.py:86(Sequence)
1000 0.029 0.000 0.029 0.000 {method 'range' of
'sage.rings.integer_ring.IntegerRing_class' objects}
2 0.027 0.014 0.031 0.016 __init__.py:2(<module>)
1000 0.015 0.000 0.276 0.000 constructor.py:34(matrix)
1000 0.015 0.000 0.090 0.000 misc.py:1051(srange)
26770 0.011 0.000 0.011 0.000 {isinstance}
2000 0.006 0.000 0.006 0.000 sequence.py:463(__init__)
1000 0.005 0.000 0.005 0.000
matrix_space.py:154(__classcall__)
2000 0.005 0.000 0.009 0.000 misc.py:179(cputime)
243 0.004 0.000 0.005 0.000
function_base.py:3122(add_newdoc)
2000 0.003 0.000 0.003 0.000 {resource.getrusage}
1 0.002 0.002 0.003 0.003 polynomial.py:48(<module>)
2000 0.002 0.000 0.002 0.000
{sage.structure.element.canonical_coercion}
1 0.002 0.002 0.033 0.033 type_check.py:3(<module>)
1 0.002 0.002 0.002 0.002 chebyshev.py:77(<module>)
2000 0.001 0.000 0.010 0.000 misc.py:410(verbose)
18531/18518 0.001 0.000 0.001 0.000 {len}
...
}}}
and 5.3b2+tickets
{{{
96514 function calls (96457 primitive calls) in 0.682 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1000 0.316 0.000 0.326 0.000 {method 'echelon_form' of
'sage.matrix.matrix_rational_dense.Matrix_rational_dense' objects}
1000 0.136 0.000 0.139 0.000 matrix_space.py:1180(matrix)
1 0.050 0.050 0.683 0.683 <ipython console>:1(test1)
1000 0.043 0.000 0.043 0.000 {method 'range' of
'sage.rings.integer_ring.IntegerRing_class' objects}
2000 0.029 0.000 0.047 0.000 sequence.py:86(Sequence)
1000 0.017 0.000 0.108 0.000 misc.py:1051(srange)
1000 0.016 0.000 0.198 0.000 constructor.py:34(matrix)
26770 0.010 0.000 0.010 0.000 {isinstance}
2000 0.007 0.000 0.008 0.000 sequence.py:463(__init__)
2 0.006 0.003 0.011 0.006 __init__.py:2(<module>)
2000 0.004 0.000 0.008 0.000 misc.py:179(cputime)
1000 0.004 0.000 0.006 0.000
matrix_space.py:154(__classcall__)
2000 0.004 0.000 0.004 0.000
{sage.structure.element.canonical_coercion}
243 0.004 0.000 0.004 0.000
function_base.py:3122(add_newdoc)
2000 0.003 0.000 0.003 0.000 {resource.getrusage}
1 0.002 0.002 0.003 0.003 polynomial.py:48(<module>)
1 0.002 0.002 0.002 0.002 chebyshev.py:77(<module>)
18531/18518 0.002 0.000 0.002 0.000 {len}
1001 0.001 0.000 0.002 0.000 weakref.py:55(__getitem__)
...
}}}
so it's actually faster! (I've tried, these numbers seem representative).
I have no explanation for what is faster here. Perhaps just some
dictionary
lookups that happen faster now thanks to 'MonoDict' and 'TripleDict'.
So apart from `echelon_form` being really badly tuned for its choice of
algorithm, what do we do about this parent construction? Should sage keep
a
cache of useful finite fields and be a little more restrained in its
choice of
primes (promoting reuse of these finite fields)?
--
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/13400#comment:1>
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 post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sage-trac?hl=en.