Repository navigation
Skycoord baseframe method speedup - #6941
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. |
|
I will say that caching was quite a thorn in implementing mutability of Time, so in general 👍 on getting away from caching where possible. |
|
I spent way longer than I had intended thinking about this and looking at the current code. Specifically I was thinking about the idea of I think the key to all this is to make it possible to instantiate SkyCoord from frames with data if there are no positional arguments (to avoid data merging issues). That is, the following should work: If we can get this to work and if we can have a shortcut for when there are no args to detect this, then this could speed things up significantly in SkyCoord's |
|
@astrofrog - I agree with you that it would be much nicer to go through Anyway, I don't have the time right now, but I'll try to make an alternative PR that uses |
|
I apparently need to carve out some time to look at this in more detail. The following however made me want to comment sooner rather than later:
SunPy does some rather funky and important things in the I also agree wholeheartly with this:
The constructor of |
Well we did have agreement that SkyCoord would be extremely liberal what it accepted, and that's what I coded. 😄 But maybe now we do need to revisit the original design decision? Perhaps a new parameter or a new subclass to heavily restrict the allowed inputs for improved performance? |
|
@Cadair - it don't think it is that bad -- @eteq, @adrn and I discussed this at some length, and we think it can be made more clear what happens (and probably faster at the same time) without too much effort, partially by moving quite a bit of stuff to the frame transformation graph. Also, part of the problem is that we kept adding things without revisiting the structure. More on topic of this PR: the good part of the normal |
😆 yeah it's not. I think the
🤔 makes me wonder if I should write more of them. Maybe a test that uses the |
|
@Cadair - further tests are always good, though you guys do sufficient testing with astropy-dev that I think we're effectively already including your tests! |
|
I think it's fine to have the initializer be very flexible, but maybe it's a question of clearly documenting in what order things happen and having ways of taking shortcuts for performance. That is, we do the fast checks first and only if things don't match do we then move to the next level of slowness, and so on. Then e.g. initializing a SkyCoord with a frame with data and nothing else would be super fast whereas initializing with coordinates as strings and with lots of attributes would be slower. |
taldcroft
left a comment
There was a problem hiding this comment.
The changes in SkyCoord._apply look sensible to me. Overall I'm bowing out on this one to current coordinates maintainers / developers.
I think there's probably a lot of space in the Probably the thing to do is try to do this approach @astrofrog is suggesting first along with the larger clean-up, but then if it becomes clear that even with restructuring we can't eke out more performance, then we try the more "restrictive" option? (comments on this PR itself next) |
eteq
left a comment
There was a problem hiding this comment.
I share @mhvk et al's concern about the bypassing of __init__... The key question is this: do we think we can re-structure the SkyCoord initializer in 3.1 to make it so that this is no longer necessary? If so we may just want to live with the costs in __init__ for 3.0 so that we don't bypass it now but then just bring it back in 3.1...? But if the only folks who care right now are SunPy, and @Cadair is happy with this, then I think this is an acceptable "stopgap".
If this is to go in, though, it definitely needs a changelog entry that points out that __init__ is now getting bypassed in these cases (probably under "API changes"?), as that's a subtle but important API change. This also needs a rebase now that #6940 is in (which this includes as it stands).
|
|
||
| representation_component_units = property(get_representation_component_units) | ||
|
|
||
| def _replicate(self, data, copy=False, **kwargs): |
There was a problem hiding this comment.
I know this is private, but it might be useful to provide a docstring with parameters here, given how convoluted this logic can get...?
Currently, getting an item or slice causes a new representation object to be created, which does a lot of tests on the inputs, which are not necessary, since the data is prevalidated. By skipping this, we can speed up accessing a single element by a factor of about 4.
Partially by splitting out replication stuff, which needlessly confuses it, and which still has to go through __init__.
1442114 to
f5f4f91
Compare
|
@eteq - OK, I rebased, added a docstring to |
|
@eteq - are you happy with the changes? |
|
I'm afraid I won't have time to do a thorough review of this today, but I just want to say that conceptually I agree with @eteq here:
But I also agree with the more practical point:
So: that's a long way of saying that I'm fine if this goes in, but would also be fine leaving as is and just promising performance improvements with 3.1 |
|
Alright, I think it's worth the performance improvement, so will merge, but I'm going to make a new issue and milestone it for 3.1 to make a final decision on " |
This builds on #5598 and #6940 to speed up item access in
SkyCoordby a factor of almost 50. The main change is for_applyto create a new instance itself, and fill it as appropriate, i.e., to avoid the extremely long and slow parsing and error checking in the__init__methods ofBaseRepresentation,BaseFrame, andSkyCoordinate. To me, this is a rather striking lesson in the dangers of trying to be overly generous in what to accept; this would not have been a problem if the various ways of initialization all had their own class methods.What I am still very unsure about, however, is whether the current approach, of just doing
__new__in_applyand fill the object, is the best one. It does mean that if people do something important in__init__in a subclass, that now gets skipped. Indeed, you'll see I already have to copy over some cached information fromself... Furthermore, I had to makeBaseFrame.realize_framego through a new_replicatemethod to ensure I wouldn't get hit by a regression test for SunPy that changes the default representation in__init__; #6236. While this is much cleaner, it does make one wonder about other possible regressions.As an alternative to just replicating the actual stuff
__init__does, one could imagine passing on an argument to__init__that states all arguments are OK and default (say,copy=None) or perhaps making a new class methodfrom_vetted_dataor so. Suggestions most welcome!EDIT: I do think this can and arguably should be done by rewriting
__init__such that it is fast for simple & correct data. In a way, this PR should be seen as showing what is possible, not necessarily as what I would like merged. But we definitely should not merge any speed-ups that work by caching before we actually have gone over these classes properly and removed the obvious bottlenecks.