-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Refactor indexing to use Varargs and ReshapedArrays #16251
Conversation
37233cd
to
d2ac8f6
Compare
## LinearFast Scalar indexing: canonical method is one Int | ||
_getindex(::LinearFast, A::AbstractArray, ::Int) = error("indexing not defined for ", typeof(A)) | ||
_getindex(::LinearFast, A::AbstractArray, i::Real) = (@_propagate_inbounds_meta; getindex(A, to_index(i))) | ||
function _getindex{T,N}(::LinearFast, A::AbstractArray{T,N}, I::Vararg{Real,N}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My impression is that this is still slow without @inline
, because of the lack of call specialization (which will hopefully happen someday, see #16159).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, nvm, next line 😦
An awe-inspiring simplification---I really like how you've used the new |
I'm still having difficulty getting SubArray to play along with this whole scheme. I think I finally got something that will pass tests now… it'll be very interesting to see what it's performance is like, since we end up with ridiculously complicated nested structures from taking linear views into SubArrays. |
Does Nanosoldier hear me from within a bulleted list? Let's try it on a line by itself: @nanosoldier |
Sorry, Nanosoldier isn't back online yet. I'm bringing it back up tomorrow, I'll trigger the build here once it's available. |
Looks like you're almost there. The BitArray tests are definitely hard-core when it comes to specifying the return types. The same issue came up in #15449. |
Yup, it's a really wonderful catch and a simple fix — we don't want to let the reshaped view escape! I've cleaned things up so tests should pass at every stage along the way (despite how GitHub wants to display the order of the commits). Thanks @jrevels. In the meantime, I did a quick run of test/perf/array.jl… and it's looking quite good. The huge wins are linear indexing into linear slow arrays, and there's a big win for logical indexing into BitArray, too. No tests are more than 20% worse, and there's not an obvious pattern to the 17 that are more than 10% worse. |
Any thoughts on the spelling of the
|
I don't like the |
Another option is |
The maiden voyage of the new Nanosoldier configuration: @nanosoldier |
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @jrevels |
Seems like I need to raise tolerances for the EDIT: Seems like the status icon didn't update even though the job is finished - I'll have to look into that... |
@jrevels, that also seems a lot faster than I remember it. If so, nice! |
Thanks! The new execution strategy we're using has cut down the runtime for a single pass of the full suite from ~4 hours to around ~30 minutes; expected turnaround time for full PR builds like this one is down from ~11 hours to ~3 hours. |
Thanks so much @jrevels, this is great. I'm okay with the current |
else | ||
:(@_propagate_inbounds_meta; (merge_indexes(V, idxs, subidxs),)) | ||
:(error("mismatched index dimensionalities")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any way of making this more descriptive if it ever happens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to call this "rank" instead of "dimensionality"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I should probably enforce this in the constructor. The whole point here is to ensure that the number of indices stored in the SubArray matches the number of dimensions in the parent array. Even if you do manage to create a corrupted SubArray, though, you'd almost certainly hit a missing method in the other reshape
reindex
signatures long before you ever get here.
We've typically stayed away from using the word rank to mean ndims
because of rank
.
and add a better error message
I seem to be consistently getting a failure in the core.jl test after this merged. It sometimes goes away with inlining, and can be prevented with julia> [][] # should throw a bounds error
ERROR: UndefRefError: access to undefined reference
in getindex(::Array{Any,1}) at abstractarray.jl:949
in eval(::Module, ::Any) at ./boot.jl:226 julia> @code_typed [][]
1-element Array{Any,1}:
LambdaInfo for getindex
:(begin # abstractarray.jl, line 424:
$(Expr(:meta, :propagate_inbounds)) # abstractarray.jl, line 425:
$(Expr(:inbounds, false)) # abstractarray.jl, line 447: # abstractarray.jl, line 448: # abstractarray.jl, line 449:
$(Expr(:boundscheck, true))
true
$(Expr(:boundscheck, :pop)) # abstractarray.jl, line 450:
$(Expr(:inbounds, true))
$(Expr(:inbounds, false))
(Base.arraysize)(A,1)::Int64 # abstractarray.jl, line 949:
$(Expr(:inbounds, :pop))
r = (Base.arrayref)(A,1)
$(Expr(:inbounds, :pop)) # abstractarray.jl, line 451:
$(Expr(:inbounds, :pop))
return r
end) |
Ah, wait. Maybe it's not the direct fault of this PR. The zero-arg bounds check test is wrong: base/abstractarray.jl
138 _internal_checkbounds(A::AbstractArray) = true |
Where does that go wrong? (I ask because I'm in the middle of revamping |
This is unrelated, but It's unfortunately another case of a probably-should-be-ambiguous dispatch (#11242 (comment)): julia> @which Base._getindex(Base.LinearFast(), A)
_getindex(::Base.LinearFast, A::AbstractArray, I::Real...) at abstractarray.jl:447
# I'd expect:
# _getindex{T,N}(::LinearFast, A::AbstractArray{T,N}, I::Vararg{Real,N}) at abstractarray.jl:438 |
Oh, duh, right. |
the "obvious" fix seems to work for, is there any reason to do something more complicated: diff --git a/base/abstractarray.jl b/base/abstractarray.jl
index 0972cae..6522bc3 100644
--- a/base/abstractarray.jl
+++ b/base/abstractarray.jl
@@ -135,7 +135,7 @@ throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I)))
checkbounds(A::AbstractArray, I...) = (@_inline_meta; _internal_checkbounds(A, I...))
# The internal function is named _internal_checkbounds since there had been a
# _checkbounds previously that meant something different.
-_internal_checkbounds(A::AbstractArray) = true
+_internal_checkbounds(A::AbstractArray) = _internal_checkbounds(A,1)
_internal_checkbounds(A::AbstractArray, I::AbstractArray{Bool}) = size(A) == size(I) || throw_boundserror(A, I)
_internal_checkbounds(A::AbstractArray, I::AbstractVector{Bool}) = length(A) == length(I) || throw_boundserror(A, I)
function _internal_checkbounds(A::AbstractArray, I1, I...) |
LGTM. Presumably could also just check |
This puts some of the amazing new infrastructure through its paces, on our way towards #14770. Using a combination of
Vararg
andReshapedArrays
, we can always ensure that scalar, non-scalar, and view indexing all get indexed at an optimal dimensionality. This is slightly more verbose than would otherwise be necessary — it explicitly defines separate methods to support the general linear indexing cases that are on their way towards deprecation.TODO:
runbenchmarks(ALL)
reshape(A, Val{N})
method. The currentimplementationname is just a stop-gap… as I was completing this I was thinking about doing something like this: