Commit ddcfc9a
Release Manager
gh-36068: Speed-up matrix construction by ensuring MatrixArgs type MA_ENTRIES_ZERO
There are many ways to specify entries when creating a matrix, which are
handled in `args.pyx` with `MatrixArgs` class. It has been discussed
before why allowing `entries=None` in the matrix creation methods can be
beneficial in terms of performance
(#11589 ,
#12020). This input
`entries=None` is required to yield the zero matrix. For example:
`matrix(QQ, 4, 4)` creates the zero matrix. This corresponds to the type
`MA_ENTRIES_ZERO` in `args.pyx`. When one passes a value, e.g.
`matrix(QQ, 4, 4, 2)`, then one gets a diagonal matrix with `2` on the
diagonal; this corresponds to the type `MA_ENTRIES_SCALAR` in
`args.pyx`.
Currently, doing something like `matrix(QQ, 4, 4, 0)` will pick
`MA_ENTRIES_SCALAR`, and therefore will build the matrix and fill the
diagonal with zero. [Behind the scenes, there is still some
acknowledgement that this is not the usual scalar matrix case, since
this will not fail if the matrix is not square (`matrix(QQ, 4, 5, 0)`
will not fail, but `matrix(QQ, 4, 5, 1)` will). But this is still not
seen as `MA_ENTRIES_ZERO`.] This PR ensures the type `MA_ENTRIES_ZERO`
is picked in this situation. This improves performance and solves some
issues, as noted below. This PR also fixes the related issue
#36065 .
In fact, `entries=None` is the default in the `__init__` of all matrix
subclasses presently implemented. It also used to be the default when
constructing a matrix by "calling" a matrix space, until
#31078 and https://github.com/sag
emath/sage/commit/cef613a0a57b85c1ebc5747185213ae4f5ec35f2 which changed
this default value from `None` to `0`, bringing some performance
regression.
Regarding this "calling the matrix space", this PR also solves the
performance issue noted in
#35961 (comment) ,
where it was observed that in the following piece of code:
```
sage: space = MatrixSpace(GF(9001), 10000, 10000)
sage: %timeit zmat = space.matrix()
18.3 µs ± 130 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops
each)
sage: %timeit zmat = space()
12.1 ms ± 65.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
```
the called default is not the same. `space.matrix()` directly
initializes the matrix through `entries=None`, but on the other hand
`space()` actually calls the constructor with `entries=0`. This
performance regression comes from
#31078 and https://github.com/sag
emath/sage/commit/cef613a0a57b85c1ebc5747185213ae4f5ec35f2 , where the
default for construction from the matrix space was changed from `None`
to `0`. This cannot be easily reverted: this is now the default in the
`__call__` of the `Parent` class. So this PR does not directly revert
the call default to `None` somehow, but the result is very close in
effect (read: the overhead is small). Unchanged: through the parent
call, `0` is converted to the base ring zero, which is passed to the
constructor's `entries` which is then analyzed as `MA_ENTRIES_SCALAR`.
Changed: the code modifications ensure that soon enough it will be
detected that this is in fact `MA_ENTRIES_ZERO`. The small overhead
explains why, despite the improvements, construction with `None` is
still sometimes slightly faster than with `0`.
Below are some timings showing the improvements for some fields. Also,
this PR merged with the improvements discussed in
#35961 will make the above
timings of `space.matrix()` and `space()` be the same (which means a
speed-up of a factor >500 for this call `space()`...). The measurement
is for construction via calling the matrix space: `None` is
`space(None)`, `Empty` is `space()`, `Zero` is `space(0)`, and `Nonzero`
is `space(some nonzero value)`.
```
NEW TIMES
field dim None Empty Zero Nonzero
GF(2) 5 2.3e-06 3.2e-06 3.6e-06 3.2e-06
GF(2) 50 2.4e-06 3.3e-06 3.6e-06 5.8e-06
GF(2) 500 3.6e-06 4.5e-06 4.8e-06 3.1e-05
GF(512) 5 2.6e-05 2.8e-05 2.9e-05 2.9e-05
GF(512) 50 2.6e-05 2.9e-05 2.9e-05 4.0e-05
GF(512) 500 3.7e-05 3.8e-05 3.9e-05 1.6e-04
QQ 5 2.2e-06 3.3e-06 3.4e-06 3.2e-06
QQ 50 8.0e-06 9.2e-06 9.4e-06 1.2e-05
QQ 500 6.1e-04 6.3e-04 6.4e-04 6.7e-04
OLD TIMES
field dim None Empty Zero Nonzero
GF(2) 5 2.3e-06 3.5e-06 3.6e-06 3.7e-06
GF(2) 50 2.4e-06 6.0e-06 6.1e-06 6.0e-06
GF(2) 500 3.6e-06 3.0e-05 3.0e-05 3.0e-05
GF(512) 5 2.5e-05 2.8e-05 2.9e-05 2.9e-05
GF(512) 50 2.5e-05 3.9e-05 4.0e-05 4.0e-05
GF(512) 500 3.5e-05 1.5e-04 1.5e-04 1.6e-04
QQ 5 2.2e-06 3.5e-06 3.7e-06 3.7e-06
QQ 50 7.9e-06 1.2e-05 1.2e-05 1.2e-05
QQ 500 6.4e-04 6.9e-04 6.9e-04 6.9e-04
```
Code used for the timings:
```
time_kwds = dict(seconds=True, number=20000, repeat=7)
fields = [GF(2), GF(2**9), QQ]
names = ["GF(2)", "GF(512)", "QQ"]
vals = [GF(2)(1), GF(2**9).gen(), 5/2]
print(f"field\tdim\tNone\tEmpty\tZero\tNonzero")
for field,name,val in zip(fields,names,vals):
for dim in [5, 50, 500]:
space = MatrixSpace(field, dim, dim)
tnone = timeit("mat = space(None)", **time_kwds)
tempty = timeit("mat = space()", **time_kwds)
tzero = timeit("mat = space(0)", **time_kwds)
tnonz = timeit("mat = space(1)", **time_kwds)
print(f"{name}\t{dim}\t{tnone:.1e}\t{tempty:.1e}\t{tzero:.1e}\t{
tnonz:.1e}")
```
### 📝 Checklist
- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation accordingly.
### ⌛ Dependencies
URL: #36068
Reported by: Vincent Neiger
Reviewer(s): Matthias Köppe, Vincent Neiger
2 files changed
+35
-7
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
| |||
847 | 848 | | |
848 | 849 | | |
849 | 850 | | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
850 | 869 | | |
851 | 870 | | |
852 | 871 | | |
| |||
924 | 943 | | |
925 | 944 | | |
926 | 945 | | |
| 946 | + | |
927 | 947 | | |
928 | | - | |
929 | | - | |
930 | | - | |
931 | | - | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
932 | 958 | | |
933 | 959 | | |
934 | 960 | | |
| |||
1219 | 1245 | | |
1220 | 1246 | | |
1221 | 1247 | | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
1222 | 1252 | | |
1223 | 1253 | | |
1224 | 1254 | | |
1225 | 1255 | | |
1226 | | - | |
1227 | | - | |
1228 | 1256 | | |
1229 | 1257 | | |
1230 | 1258 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
811 | 811 | | |
812 | 812 | | |
813 | 813 | | |
814 | | - | |
| 814 | + | |
815 | 815 | | |
816 | 816 | | |
817 | 817 | | |
| |||
0 commit comments