Conversation
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
Click here to view all benchmarks. |
|
Hey, @Graciaaa3, is it actually ready for review? I'm a little bit confused because the PR is marked as a draft, but I'm selected as a reviewer. |
Hi @hombit, sorry for the confusion, it's ready for review! I put in a draft PR since I haven't finish writing the tests, but the code part and the tutorial notebook are finished. |
There was a problem hiding this comment.
Very deep and useful work, thank you!
I'm not 100% sure how the numba compilation pipeline works, but I guess that our @njit functions would be recompiled for every new user's func. For the same reason, I guess it is not worth asking the user to pre-jit-compile their function; we should just try to do it ourselves, maybe conditionally on some keyword argument, e.g. engine=None, like in pandas' apply method. Other reasons to make it conditional on a new argument: 1) your benchmarks show that speed-up is not guaranteed, so the choice should be more explicit, 2) if the user wants to use a numba-compiled func, but not a numba-compiled map_rows, for example for an arbitrary number of columns, we absolutely should allow it, and the warning would be confusing.
I'm wondering how hard it would be to generalize to an arbitrary number of columns using a bit of Python metaprogramming magic?
Thank you for making the notebook and the plots, they are great and very informative! One cosmetic issue: could you please set ylim to [0; None], so 0 is always visible? I would also like to see how much time jit-compilation takes, because it is not clear if it is actually worth it for single-frame use, and what guidelines we should give LSDB users about using it.
Tests would be appreciated.
| iterators.append(self[col]) | ||
| else: | ||
| iterators.append(self[layer].array.iter_field_lists(col)) | ||
| # check if func is jitted by numba and fits the criteria |
There was a problem hiding this comment.
There is a lot of code here, maybe worth refactoring to a new helper function/method?
dougbrn
left a comment
There was a problem hiding this comment.
Thank you for all your work on this! Just have an initial comment here, and then I'll look through the tutorial notebook separately
| from nested_pandas.series.nestedseries import NestedSeries | ||
| from nested_pandas.series.packer import pack, pack_lists, pack_sorted_df_into_struct | ||
| from nested_pandas.series.utils import is_pa_type_a_list | ||
| from numba.core.registry import CPUDispatcher |
There was a problem hiding this comment.
You'll need to add numba as a dependency to pyproject.toml, and that should hopefully allow the docs to build.
I'm a little conflicted about adding it as a core dependency, but I think it's the right choice for now.
There was a problem hiding this comment.
I'm agree about keeping numba optional. It always causes a lot of weird dependency problems with CPython/numpy upgrades.
|
Please see this for notebook comments: https://app.reviewnb.com/lincc-frameworks/nested-pandas/blob/map_rows-njit/docs%2Fpre_executed%2Fnjit_map_rows.ipynb%2F/discussion I think the notebook is looking really good! |
Thanks for the comment! I agree we should allow optional njit if the user doesn't wants to use it. I looked into pandas' And would we want to support a similar feature of compiling python custom func with njit inside Also, for the time it takes for jit-compiling custom function and/or |
I don't have a strong opinion. I'm OK with supporting callables only, e.g.
No, but I also believe that any
Yes, I think even a single compilation + single jitted map_rows evaluation to pure Python map_rows evaluation ratio would be useful. Again, it is interesting to see these numbers because njitted map_rows would be recompiled for every new Justification that function argument causes recompilation of an outer function# Feel free to set NUMBA_DEBUG_JIT=1 to see compilation logs
from numba import njit
def inner1(x, y):
return x**0.2 + y**0.3
inner1_compiled = njit(inner1)
inner1_compiled(1.0, 1.0)
def inner2(x, y):
return x**0.3 - y**0.2
inner2_compiled = njit(inner2)
inner2_compiled(1.0, 2.0)
def outer(f, x, y):
return f(x, y) + x**y
outer_compiled = njit(outer)
outer_compiled(inner1_compiled, 1.0, 1.0)
## Check if recompilation is caused for any new input function
## Using the same function argument is fast, no recompilation I guess
%time outer_compiled(inner1_compiled, 2.0, 2.0)
# Wall time: 19.8 μs
## Using a different function is slow: note millisecond vs microseconds
%time outer_compiled(inner2_compiled, 2.0, 2.0)
# Wall time: 16.4 ms |
e951a59 to
9f5954b
Compare
Change Description
Adding njit version of
map_rows(originallyreduce) to achieve potential speedup for the row-wise operation. This has a factored out module ofnjit_funcs.pythat contains njit-decorated helper methods.Adding a documentation notebook for njit
map_rowsusage with examples and benchmark results.Closes #277
Solution Description
map_rows.map_rowsexecution as the default implementation if njit doesn't support.Code Quality
Project-Specific Pull Request Checklists
New Feature Checklist
I will add the tests to cover added logic and update the documentation for
map_rowsafterward.