-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Fix type instability when using custom indices #21599
Conversation
This might be worth having, but first let me ask: can't you just specialize Since you're checking performance, it's worth noting that your The reason I'm mentioning these performance tips is that there's some chance (especially for high dimensions) that a |
My current workaround is to specialize Thanks for the tip on CartesianRange, I had forgotten about that. |
That's a legitimate concern, and there are other places where I've had to be careful about constructing the correct type. So having some kind of trait for this might be the right thing to do. However, the |
Would using indicestype{ScalarT,N,D}(::Type{Array{ScalarT,N}},::Type{Val{D}}) |
@@ -37,7 +37,7 @@ Base.OneTo(6) | |||
""" | |||
function indices(A::AbstractArray{T,N}, d) where {T,N} | |||
@_inline_meta | |||
d <= N ? indices(A)[d] : OneTo(1) | |||
d <= N ? indices(A)[d] : one(indicestype(typeof(A),d)) |
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.
Personally, I would rather prefer that functions in base like broadcast
never needed to call this function in this (second/negative) case... I.e. If custom types neglect to implement indexing operations other than linear or exactly N
dimensional Cartesian, then it would be great if everything respected that and just worked (this surprised me in some point in the past).
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.
That would indeed simplify things. It's what I'm doing right now as a workaround. So far I noticed this breaks display(A)
, but I'm sure that's fixable.
Yes, it seems that I guess the main thing I'd consider is whether |
I can't really think of any other use, to be honest. Following on @andyferris 's comment, I wonder what would happen if the branch on |
That's an interesting suggestion, and given that function foo(A::AbstractVecOrMat)
m, n = size(A, 1), size(A, 2)
... However, this also causes some performance gotchas (e.g., JuliaMath/Interpolations.jl#154) because of the branch. I wonder if it would be better to have a more composable solution. For example, two that work now are function foo(A::AbstractVecOrMat)
sz = size(A)
m, n = sz[1], length(sz) == 2 ? sz[2] : 1
... or function foo(A::AbstractVecOrMat)
m, n = Base.fill_to_length(size(A), 1, Val{2})
... The function foo(A::AbstractVecOrMat)
inds = indices(A)
inds1, inds2 = Base.fill_to_length(inds, oftype(inds[1], Base.OneTo(1)), Val{2})
... Making all this easier to use seems like a worthwhile exercise. |
As a bit of a indices-noob - how do implied singleton dimensions work in the indices world? E.g. does/can/should If so, then I'm wondering if there is no well-defined answer for what that implied index is for all |
OTOH a type like This is a tricky one, but I think "missing" early dimensions are a thing that we should try to support correctly. |
I must admit I didn't consider broadcasting at all. The implied indexing does seem counter-intuitive, with or without this PR: # Column vector:
v = ZeroView([1,2,3])
v[0] # gives 1
v[0,0] # bounds error at abstractarray.jl:372
v[0,1] # gives 1
# Row vector:
v = ZeroView([1 2 3])
v[0] # bounds error, because linear indices are 1-based
v[0,0] # gives 1
v[1,0] # bounds error The second bounds error is because linear indices are 1-based and has nothing to do with this PR. The first one comes from line 404 or 408 in abstractarray.jl I think, and it might be fixable (if deemed necessary) in the context of the current PR by replacing the |
I don't have any good answers here, but I can similarly point at my own failed experiments at trying to use custom indices types to support the Axis-propagation behavior in AxisArrays (JuliaArrays/AxisArrays.jl#58 and JuliaArrays/AxisArrays.jl#81). That second attempt is failing due to type assertions that work around these sorts of instabilities within broadcasting code. In the context of AxisArrays, I think the "right" behavior is to replace the singleton axes with the broadcasted one. I'm not sure how generalizable that choice is, but it implies to me that any length-1 dimension can support broadcasting, regardless of its indices/axes. |
My feeling is that is correct, but...
... note that this sometimes might be ambiguous - if I broadcast two vectors with indices (It's definitely a bit of a strange case, but if the length of the vectors are set at run-time, then I'm worried we won't be able to avoid odd behavior in a small number of corner cases when lengths of indices happen to equal 1). |
This got fixed along the road to 1.0, it seems! |
This adds a new method
indicestype
to overcome a type instability when using non-conventional indices. Example:This gives a time of 0.18 s with 16 M allocations on the
benchmark_fill_mat
function. After the fix and specializingindicestype
as follows:Then the new timing is 0.02 s with 4 allocations.
Context:
https://discourse.julialang.org/t/multidimensional-arrays-with-0-based-indices/3325/7
cc @timholy