Conversation
|
👋 Thank you for your draft pull request! Do you know that you can use |
|
@mhvk - As mentioned in another PR, this seems to be basically a complete rewrite. Which is OK, but we do need to maintain the same external API. I have code that uses this API. I do worry slightly that the C code is going from pretty simple to something that I honestly can't understand nor review beyond checking tests. |
|
@taldcroft - OK, I'll make sure that the format used to indicate how to parse remains supported. Note that the actual parsing code is not really changed, it is just that the iteration is now done differently. Most of the rest on the C side is the boilerplate needed for ufuncs. One other advantage of the ufunc is that it goes towards being able to support things like dask arrays in Time. |
|
OK, sounds good! Thanks for bring this into the future.
This is a little ambiguous and I'm not sure if you want to introduce support for another format? |
7fe2f47 to
6b64588
Compare
|
@taldcroft - OK, I pushed the new version, which simply uses the old format, converting it as needed for the gufunc in I did add a test that if a user defines their own |
| npy_bool break_allowed; | ||
| }; | ||
|
|
||
| // Distutils on Windows automatically exports ``PyInit__parse_times``, |
There was a problem hiding this comment.
Hmm, clearly this is needed for ufuncs also. Going to put it back...
There was a problem hiding this comment.
Same question, why is this no longer needed?
6b64588 to
87d6d4c
Compare
|
@taldcroft - OK, tests pass now (or, rather, the failures are unrelated). While considering the windows failure (which turned out to be a forgotten bit in |
taldcroft
left a comment
There was a problem hiding this comment.
I just started looking at this and made some trivial comments.
The real question I have is what is the driver for the big changes in parse_ymdhms_times which turned into parse_loop. If I understand they are doing the same thing and the main point of this PR was to put in the gufunc machinery outside of that inner loop.
What I note is that the original parse_ymdhms_times is extremely simple and it is pretty straightforward to understand the logic. Your new version is much more programmatically complex and I find it difficult to verify correctness by just reading.
Things like changing an explicit time structure to char **out = args+1; reduces readability IMO (and I prefer for clarity the pre-initialization to setting values within the logic). I don't think that the strategy of trying to loop over the elements in a generic way (vs. the original "unrolled loop") is winning anything for performance.
| SRC_FILES = [os.path.join(C_TIME_PKGDIR, filename) | ||
| for filename in ['src/parse_times.c']] | ||
|
|
||
| if sys.platform.startswith('win'): |
There was a problem hiding this comment.
What changed so that these are no longer necessary? I know that it failed to build on Windows when I was developing this change originally.
There was a problem hiding this comment.
I removed it initially since I have not needed it for other ufuncs (like in erfa), but must admit I don't really know -- I'm essentially relying on CI here...
| npy_bool break_allowed; | ||
| }; | ||
|
|
||
| // Distutils on Windows automatically exports ``PyInit__parse_times``, |
There was a problem hiding this comment.
Same question, why is this no longer needed?
| "standard time strings." | ||
| #define CREATE_PARSER_DOCSTRING \ | ||
| "create_parser()\n\n" \ | ||
| "Create a gufunc that parsers according to the passed in parameters.\n\n" \ |
|
One interesting thing is that I did some profiling to check the performance. For large input arrays (e.g. 1e6) there is no difference in timing. For small arrays (10), I see that your new version is substantially faster: That's great. Do you have an idea where that is coming from? |
|
@taldcroft - thanks for doing the profiling - I meant to do that myself, but never got around to it. I had actually been worried that the ufunc would be slower for small N, given the what I thought would be larger call overhead. But perhaps that is offset by pre-calculating the start/stop/deliminator array? Anyway, not quite sure but happy to see it actually is a true performance improvement! |
|
On the larger question, maybe I was indeed wrong to move away from the time struct: it felt cleaner but I can see that it has become less clear. I'll think a bit more. |
|
closing in favour of #11768 |
@taldcroft - some time ago I wrote a gufunc replacement for the fast parser, in part because that means that shapes are automatically dealt with, but also because it can automatically deal with masked input (as it can be overridden with
__array_ufunc__). I also found it nice that it more cleanly separates the parsing and loading parser information, dealing with status codes, etc. I cleaned it up a bit today, so we can discuss.One thing I did then which in hindsight I probably shouldn't have, is change the format for the parser information, to a structured array. This is helpful for the ufunc, as it means I can just store the array inside, but obviously breaks API. So, at the very least I should add support for interpreting the old format. But I can also just keep the old format and convert it to my structured array format in
__init_subclass__. Anyway, before I do anything I thought I'd better let you have a look and see which one you prefer.EDIT: In this version, I replaced the time structured array with a tuple, as it seemed slightly easier (if only just in the
erfacall at the end); I have another version where the structured array is kept.