Skip to content

Support for boxing SliceLiteral type - #7714

Merged
esc merged 11 commits into
numba:mainfrom
njriasan:nick/literal_slice_boxing
Jun 20, 2022
Merged

esc merged 11 commits into
numba:mainfrom
njriasan:nick/literal_slice_boxing

Conversation

@njriasan

@njriasan njriasan commented Jan 8, 2022

Copy link
Copy Markdown
Contributor

Support boxing directly with a SliceLiteral. This fixes the example in #7707.

guilhermeleobas
guilhermeleobas previously approved these changes Jun 6, 2022

@guilhermeleobas guilhermeleobas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi Nick, thanks for working on this.

@stuartarchibald

Copy link
Copy Markdown
Contributor

Think this needs fixing:

from numba import njit

z = slice(1, 2, 3)

@njit
def foo():
    return z

foo()

via:

diff --git a/numba/core/pythonapi.py b/numba/core/pythonapi.py
index 79ca776..6dad042 100644
--- a/numba/core/pythonapi.py
+++ b/numba/core/pythonapi.py
@@ -663,7 +663,7 @@ class PythonAPI(object):
     # Concrete slice API
     #
     def slice_new(self, start, stop, step):
-        fnty = Type.function(self.pyobj, [self.pyobj, self.pyobj, self.pyobj])
+        fnty = ir.FunctionType(self.pyobj, [self.pyobj, self.pyobj, self.pyobj])
         fn = self._get_function(fnty, name="PySlice_New")
         return self.builder.call(fn, [start, stop, step])

@stuartarchibald stuartarchibald added Effort - medium Medium size effort needed 4 - Waiting on author Waiting for author to respond to review and removed 3 - Ready for Review labels Jun 7, 2022
@njriasan

njriasan commented Jun 7, 2022

Copy link
Copy Markdown
Contributor Author

Thanks @stuartarchibald. I fixed the issue. In addition, I found a couple other places where Type was still used so I fixed them in this PR as well.

@stuartarchibald stuartarchibald left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the patch @njriasan and for reviewing @guilhermeleobas. I've given this a bit more of a review and left some comments in line, most should be relatively quick to resolve. Thanks again.

Comment thread numba/tests/test_slices.py Outdated
Comment thread numba/core/pythonapi.py Outdated
Comment thread numba/tests/test_slices.py
Comment thread numba/core/boxing.py Outdated
Comment on lines +384 to +399
slice_lit = typ.literal_value
slice_fields = []
for field_name in ("start", "stop", "step"):
field_obj = getattr(slice_lit, field_name)
field_val = (
c.pyapi.make_none()
if field_obj is None
else c.pyapi.from_native_value(
types.literal(field_obj), field_obj, c.env_manager
)
)
slice_fields.append(field_val)
slice_val = c.pyapi.slice_new(*slice_fields)
for field_obj in slice_fields:
c.pyapi.decref(field_obj)
return slice_val

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Something like this might also work?

@box(types.SliceLiteral)
def box_slice_literal(typ, val, c):
    py_ctor, py_args = typ.literal_value.__reduce__()
    serialized_ctor = c.pyapi.serialize_object(py_ctor)
    serialized_args = c.pyapi.serialize_object(py_args)
    ctor = c.pyapi.unserialize(serialized_ctor)
    args = c.pyapi.unserialize(serialized_args)
    obj = c.pyapi.call(ctor, args)
    c.pyapi.decref(ctor)
    c.pyapi.decref(args)
    return obj

avoids needing to add slice_new to the pyapi too.

@njriasan njriasan Jun 14, 2022 •

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.

@stuartarchibald I'm happy to update this code if you would like but I don't understand why this change is necessary/preferred. Is there a concern that creating native literals will have gaps/performs worse?

Comment thread numba/tests/test_slices.py
Comment thread numba/core/base.py
@stuartarchibald

Copy link
Copy Markdown
Contributor

Thanks @stuartarchibald. I fixed the issue. In addition, I found a couple other places where Type was still used so I fixed them in this PR as well.

@njriasan many thanks for fixing the other places where Type was used.

@stuartarchibald stuartarchibald self-assigned this Jun 8, 2022
@njriasan

Copy link
Copy Markdown
Contributor Author

@stuartarchibald I made all of the requested changes except the serialization change to boxing because I don't understand the benefit of the change. I'm happy to refactor the code but I'd like to understand the reason for the change.

I also made one additional change to Literal.literal_type so IntegerLiteral will show the correct error message when encountering the integer overflow issue, but I didn't see a need to add a test.

@sklam sklam assigned esc and unassigned stuartarchibald Jun 14, 2022
@esc

esc commented Jun 16, 2022

Copy link
Copy Markdown
Member

I also made one additional change to Literal.literal_type so IntegerLiteral will show the correct error message when encountering the integer overflow issue, but I didn't see a need to add a test.

I think it would be good to add a test using the reproducer from #7714 (comment) -- if feasible.

@esc

esc commented Jun 16, 2022

Copy link
Copy Markdown
Member

@stuartarchibald I made all of the requested changes except the serialization change to boxing because I don't understand the benefit of the change. I'm happy to refactor the code but I'd like to understand the reason for the change.

I managed to consult Stuart in an OOB conversation today and the idea is that the suggested code is simpler and easier to maintain. There are not many references to clean up and we don't need to add the slice_new wrapper, so we are adding less code. It may result in a minor performance hit but is probably negligible and code simplicity is likely preferable. My suggestion would be to make the suggested change and remove the slice_new wrapper as well. Thank you!

@esc esc 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'm helping out with the review in order for the PR to make it into 0.56 -- I reviewed all the comments and checked that all of @stuartarchibald 's comments have been addressed, except the comment about the core boxing implementation. I also left a comment regarding a potential test that could be added.

I think once these two changes have been made this is ready to merge. Thank you!!

PS: if you are running out of time, please do let me know, I'd be happy to volunteer to make the requested changes.

@njriasan
njriasan requested a review from esc June 17, 2022 05:30
@njriasan

Copy link
Copy Markdown
Contributor Author

@esc I think I have addressed all of the comments.

@esc

esc commented Jun 20, 2022

Copy link
Copy Markdown
Member

@njriasan thank you for the changes. I have given the following two commits a quick performance test:

using 2b0a05943cc17bf1780a0e7f4a53d84dbe6ae592

In [2]: from numba import njit

In [3]: z = slice(1, 2, 3)

In [4]:         @njit
   ...:         def foo():
   ...:             return z
   ...:

In [5]: foo()
Out[5]: slice(1, 2, 3)

In [6]: %timeit foo()
74.3 ns ± 3.04 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

And with a476fa5c2abb4057ae27159f3ee8388db9922b3e:

In [1]: from numba import njit

In [2]:         @njit
   ...:         def foo():
   ...:             return z
   ...:

In [3]: z = slice(1, 2, 3)

In [4]: foo()
Out[4]: slice(1, 2, 3)

In [5]: %timeit foo()
529 ns ± 15.7 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

So that would indicate a performance hit -- but since this is in the nano-second range, I am not sure how reproducible these microbenchmarks are and how influential they will be.

@njriasan

Copy link
Copy Markdown
Contributor Author

@esc I think since a slice won't ever scale beyond 3 elements and we are talking about nanoseconds, this is probably fine. However, I also don't consider the original implementation to be too complicated, so I would be fine with either decision.

@njriasan

Copy link
Copy Markdown
Contributor Author

@esc I think since a slice won't ever scale beyond 3 elements and we are talking about nanoseconds, this is probably fine. However, I also don't consider the original implementation to be too complicated, so I would be fine with either decision.

I just saw your message on gitter. I agree the performance difference seems fine.

@esc esc added 5 - Ready to merge Review and testing done, is ready to merge and removed 4 - Waiting on author Waiting for author to respond to review labels Jun 20, 2022
@esc

esc commented Jun 20, 2022

Copy link
Copy Markdown
Member

@esc I think since a slice won't ever scale beyond 3 elements and we are talking about nanoseconds, this is probably fine. However, I also don't consider the original implementation to be too complicated, so I would be fine with either decision.

I just saw your message on gitter. I agree the performance difference seems fine.

OK, awesome, marking as Ready To Merge (RTM) now.

@stuartarchibald

Copy link
Copy Markdown
Contributor

@njriasan many thanks for your work on this, @esc thanks for the review efforts.

@esc

esc commented Jun 20, 2022

Copy link
Copy Markdown
Member

Thank you for the patch!

@esc
esc merged commit c237ec5 into numba:main Jun 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

5 - Ready to merge Review and testing done, is ready to merge Effort - medium Medium size effort needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants