Skip to content

Speed up creation of simple composite units - #7649

Merged
eteq merged 6 commits into
astropy:masterfrom
mhvk:unit-speed-up-composite-creation
Oct 30, 2018
Merged

eteq merged 6 commits into
astropy:masterfrom
mhvk:unit-speed-up-composite-creation

Conversation

@mhvk

@mhvk mhvk commented Jul 12, 2018 •

Copy link
Copy Markdown
Contributor

With this PR, the call that gets done in creating prefix units:

import astropy.units as u
%timeit u.CompositeUnit(1.e-9, [u.m], [1], _error_check=False)
# 100000 loops, best of 3: 6.77 -> 1.05 µs per loop

As this is done a lot during import, it reduces the import astropy.units time by 20% (only the units part, not including the astropy part).

EDIT: I carried on a bit with this. Now:

%timeit u.m / u.s**2
10000 loops, best of 3: 17.3 -> 8.3 µs per loop

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 the astropy.units part).

@mhvk mhvk added this to the v3.1 milestone Jul 12, 2018
@astropy-bot

astropy-bot Bot commented Jul 12, 2018 •

Copy link
Copy Markdown

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.

Comment thread astropy/units/__init__.py
from .function.logarithmic import *
from .function import magnitude_zero_points

from .decorators import *

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is just so that python -X importtime does not make it appear that importing decorators takes a long time!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth adding a comment to that effect?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Added a comment on top.

Comment thread astropy/units/core.py
if is_effectively_unity(represents.value):
represents = represents.unit
else:
# cannot use _error_check=False: scale may be effectively unity

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was actually not true: the scale was always checked (and with this PR that check is now very fast).

@mhvk
mhvk force-pushed the unit-speed-up-composite-creation branch from 245ae8b to e4308a7 Compare July 13, 2018 04:54
@mhvk mhvk changed the title Speed up creation of scaled units Speed up creation of simple composite units Jul 13, 2018
Comment thread astropy/units/utils.py
if is_effectively_unity(scale):
return 1.0

if np.iscomplex(scale): # scale is complex

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np.iscomplex is awfully slow.

Comment thread astropy/units/utils.py
This ensures that any operation involving a Fraction will use
rational arithmetic and preserve precision.
"""
a_is_fraction = isinstance(a, Fraction)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isinstance(a, Fraction) is awfully slow for the case that a is not a fraction. It might be possible to improve this further...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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" :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@pllim
pllim requested a review from adrn July 14, 2018 04:38
@mhvk mhvk mentioned this pull request Aug 4, 2018
@mhvk

mhvk commented Aug 4, 2018

Copy link
Copy Markdown
Contributor Author

@adrn - would you have a chance to review? I ask in part since in #7697 changes were proposed to units/utils.py that are no longer needed if this goes in.

Comment thread astropy/units/utils.py
This ensures that any operation involving a Fraction will use
rational arithmetic and preserve precision.
"""
a_is_fraction = isinstance(a, Fraction)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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" :)

Comment thread astropy/units/utils.py
if scale.__class__ is float:
return scale

if scale.imag:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I also found only by trial & (no) error, I added a comment.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL:

>>> (1).imag
0

Comment thread astropy/units/core.py
for p in unit.powers]
self._scale = sanitize_scale(scale)
else:
self._scale = scale

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like now the scale is only validated in the if clause above - is that ok?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All inputs get adjusted by _expand_and_gather. Since that is far from obvious, I added a comment.

Comment thread astropy/units/core.py
bases=represents.unit.bases,
powers=represents.unit.powers)
powers=represents.unit.powers,
_error_check=False)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Comment thread astropy/units/core.py
bases=s.unit.bases,
powers=s.unit.powers)
powers=s.unit.powers,
_error_check=False)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it's safe to not check because the unit is coming from a quantity, so it has already been checked?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, same logic as above. And similarly, not very important for speed.

@mhvk
mhvk force-pushed the unit-speed-up-composite-creation branch from e4308a7 to 922a854 Compare August 4, 2018 19:33
@mhvk

mhvk commented Aug 4, 2018

Copy link
Copy Markdown
Contributor Author

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.

@bsipocz

bsipocz commented Oct 28, 2018

Copy link
Copy Markdown
Member

@mhvk @adrn - what's the status of this? Can this be merged? If yes, please rebase to make sure the tests are still happy (and label with merge-if-ci-passes). Otherwise please remilestone.

@eteq eteq left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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...

@bsipocz
bsipocz force-pushed the unit-speed-up-composite-creation branch from 922a854 to c39967d Compare October 28, 2018 05:16
@mhvk

mhvk commented Oct 28, 2018

Copy link
Copy Markdown
Contributor Author

I'm happy with this as is.

@adrn

adrn commented Oct 30, 2018

Copy link
Copy Markdown
Member

Yep, fine by me!

@eteq
eteq merged commit e7f9cf6 into astropy:master Oct 30, 2018
@eteq

eteq commented Oct 30, 2018

Copy link
Copy Markdown
Member

Awesome! I think this is backport-able, do you agree @astrofrog @bsipocz ?

@astrofrog

Copy link
Copy Markdown
Member

@eteq - agree!

@bsipocz

bsipocz commented Oct 30, 2018

Copy link
Copy Markdown
Member

I'll do another backport round once #8035 is in.

@mhvk
mhvk deleted the unit-speed-up-composite-creation branch October 31, 2018 00:01
bsipocz pushed a commit that referenced this pull request Oct 31, 2018
Speed up creation of simple composite units
mhvk added a commit to mhvk/astropy that referenced this pull request Dec 11, 2018
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants