Repository navigation
Speed up creation of simple composite units - #7649
Conversation
|
Hi there @mhvk 👋 - thanks for the pull request! I'm just a friendly 🤖 that checks for issues related to the changelog and making sure that this pull request is milestoned and labeled correctly. This is mainly intended for the maintainers, so if you are not a maintainer you can ignore this, and a maintainer will let you know if any action is required on your part 😃. Everything looks good from my point of view! 👍 If there are any issues with this message, please report them here. |
| from .function.logarithmic import * | ||
| from .function import magnitude_zero_points | ||
|
|
||
| from .decorators import * |
There was a problem hiding this comment.
This change is just so that python -X importtime does not make it appear that importing decorators takes a long time!
There was a problem hiding this comment.
It might be worth adding a comment to that effect?
There was a problem hiding this comment.
Good point. Added a comment on top.
| if is_effectively_unity(represents.value): | ||
| represents = represents.unit | ||
| else: | ||
| # cannot use _error_check=False: scale may be effectively unity |
There was a problem hiding this comment.
This was actually not true: the scale was always checked (and with this PR that check is now very fast).
245ae8b to
e4308a7
Compare
| if is_effectively_unity(scale): | ||
| return 1.0 | ||
|
|
||
| if np.iscomplex(scale): # scale is complex |
There was a problem hiding this comment.
np.iscomplex is awfully slow.
| This ensures that any operation involving a Fraction will use | ||
| rational arithmetic and preserve precision. | ||
| """ | ||
| a_is_fraction = isinstance(a, Fraction) |
There was a problem hiding this comment.
isinstance(a, Fraction) is awfully slow for the case that a is not a fraction. It might be possible to improve this further...
There was a problem hiding this comment.
Can you add a comment to this effect? Something like "checking common cases int and float first because isinstance(a, Fraction) is slow". I can foresee coming back to this code in some time and wanting to "clean it up" :)
There was a problem hiding this comment.
Yes, that makes sense. It will also allow us to check easily whether the work-around is in fact needed. I think some of the abc stuff has been rewritten in C now.
| This ensures that any operation involving a Fraction will use | ||
| rational arithmetic and preserve precision. | ||
| """ | ||
| a_is_fraction = isinstance(a, Fraction) |
There was a problem hiding this comment.
Can you add a comment to this effect? Something like "checking common cases int and float first because isinstance(a, Fraction) is slow". I can foresee coming back to this code in some time and wanting to "clean it up" :)
| if scale.__class__ is float: | ||
| return scale | ||
|
|
||
| if scale.imag: |
There was a problem hiding this comment.
I was surprised that it's safe to do this after only checking is float, but I learned in checking that both int and Fraction() both have the .imag attribute!
There was a problem hiding this comment.
Since I also found only by trial & (no) error, I added a comment.
| for p in unit.powers] | ||
| self._scale = sanitize_scale(scale) | ||
| else: | ||
| self._scale = scale |
There was a problem hiding this comment.
It looks like now the scale is only validated in the if clause above - is that ok?
There was a problem hiding this comment.
All inputs get adjusted by _expand_and_gather. Since that is far from obvious, I added a comment.
| bases=represents.unit.bases, | ||
| powers=represents.unit.powers) | ||
| powers=represents.unit.powers, | ||
| _error_check=False) |
There was a problem hiding this comment.
Sorry for being a bit dense - I just want to make sure I follow the changes to _error_check and the new _error_check=False's added.
In this case, why is it safe to not check?
There was a problem hiding this comment.
My logic was that here we know represents is a Quantity, and Quantity instances ensure their unit is properly initialized as a Unit. So, represents.unit is a Unit instance which needs no further checking (note that it has to have scale, bases, and powers attributes). Now possibly this could still go wrong. Since this is not on the fast path for anything anyway, shall I just change it back?
| bases=s.unit.bases, | ||
| powers=s.unit.powers) | ||
| powers=s.unit.powers, | ||
| _error_check=False) |
There was a problem hiding this comment.
Here it's safe to not check because the unit is coming from a quantity, so it has already been checked?
There was a problem hiding this comment.
Yes, same logic as above. And similarly, not very important for speed.
e4308a7 to
922a854
Compare
|
I pushed a rebased version with more comments; am happy to revert two changes that seem to be perhaps more confusing than it is worth; just let me know. |
eteq
left a comment
There was a problem hiding this comment.
I had a look and I think this seems good to me. I also checked it locally merged with master since it's been here awhile... No problems. Since @adrn has looked at this and had a few concerns it would be good to get a 👍 from him on this, though...
This gets called all the time for Prefix Units.
922a854 to
c39967d
Compare
|
I'm happy with this as is. |
|
Yep, fine by me! |
|
Awesome! I think this is backport-able, do you agree @astrofrog @bsipocz ? |
|
@eteq - agree! |
|
I'll do another backport round once #8035 is in. |
Speed up creation of simple composite units
This is a regression introduced by astropygh-7649 and reported in astropygh-8260. Before this fix: ``` v2 = 1*u.m**2/u.s**2 (v2 ** (-1/2)).to(u.s/u.m) ``` leads to a unit conversion error because the bases are in the wrong order.
With this PR, the call that gets done in creating prefix units:
As this is done a lot during import, it reduces the
import astropy.unitstime by 20% (only the units part, not including theastropypart).EDIT: I carried on a bit with this. Now:
Import time reduced from 0.118 to 0.088 s
(Measured by
python -X importtime -c "import astropy; import astropy.units"and looking at just theastropy.unitspart).