-
-
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
Recommend to use tuples to scalarize items in broadcast expressions #35591
Conversation
Ah, it's good to know that there is a pitfall with But I think the problem with recommending using tuples is that a tuple is not always the "weakest container": julia> 1 .+ (2,)
(3,)
julia> 1 .+ Ref(2)
3 Perhaps we need an immutable version of |
It might be unrelated to the issue at hand to discuss surface syntax, but I'd think us .+ &v was even clearer |
@tkf Great point, I hadn't realized that. Such a stack allocated 0-dimensional container is indeed fairly easy to make. Here's one that's similar (but not the same as struct Scalar{T}
x::T
end
Base.getindex(s::Scalar) = s.x
Base.getindex(s::Scalar, ::CartesianIndex{0}) = s.x
Base.iterate(s::Scalar) = (s.x, nothing)
Base.iterate( ::Scalar, s) = nothing
Base.ndims(::Scalar) = 0
Base.ndims(::Type{<:Scalar}) = 0
Base.length(::Scalar) = 1
Base.eltype(::Scalar{T}) where {T} = T
Base.eltype(::Type{Scalar{T}}) where {T} = T
Base.size(::Scalar) = ()
Base.axes(::Scalar) = ()
Base.IteratorSize(::Type{<:Scalar}) = Base.HasShape{0}()
Base.broadcastable(s::Scalar) = s now julia> using StaticArrays: SA
julia> using BenchmarkTools: @btime
julia> us = (SA[1,2,3], SA[4,5,6]); v = SA[7,8,9]
3-element StaticArrays.SArray{Tuple{3},Int64,1,3} with indices SOneTo(3):
7
8
9
julia> @btime $us .+ Ref($v)
2.849 ns (0 allocations: 0 bytes)
([8, 10, 12], [11, 13, 15])
julia> @btime $us .+ Scalar($v)
0.019 ns (0 allocations: 0 bytes)
([8, 10, 12], [11, 13, 15])
julia> @btime 1 .+ Scalar(1)
0.019 ns (0 allocations: 0 bytes)
2 @mkborregaard Yeah, that syntax would be nice for this, but my understanding is that syntax proposal is controversial so I'd rather not stir up the hornets nest in this PR which is of relatively limited scope. |
I think |
It's great that you get |
The only reason I've not done this sooner myself is that a one-tuple doesn't really act like a scalar. For example:
|
Yeah, that's fair I hadn't realized that subtlety until @tkf pointed it out. What do you think about that |
I actually initially used such a type internally in initial sketches until I realized we could use |
Do you think the fact that we have constant propagation now warrants revisiting this design? |
Yes, I would love for a better pattern here. It's a common stumbling block, and to me that's a far bigger motivation for this change than the performance issue. I have a fairly strong aversion to the name |
Sounds good. Do you or anyone else have name suggestions? |
I guess |
Bump. What's needed to merge this? |
@ViralBShah The potentially blocking concern currently is that If it's okay with people, I could ammend the wording to instead say something like
and then give the same examples, that way I don't have misleading terms like "scalar". |
Done. |
It's somewhat off-topic, but I think it is somewhat inconsistent that
actually works even though
because we often mention that I guess it's another reason to have "immutable |
Funny, that's not my mental model at all. I think of tuples (and other non-array containers) as being one dimensional, even if they don't support n-dimensional access, and broadcast just extrudes over both missing and singleton dimensions. In other words, in my mental model, the array behavior is the special case (that it allows indexing by missing dimensions)... and it's not what drives broadcasting behavior. |
Ah, OK. I thought that's what you would say as (I think) I've seen you were mentioning that numbers being iterable and indexable is nice because it's compatible with broadcasting. But I agree that filling extra dimensions fits well with what we have. |
I believe this PR is now good to go. |
On bikesheding the name: instead of |
@rfourquet The discussion is happening at #35675. |
Ah thanks for the pointer! |
There's a lot of conflicting advice out there on what one should do in order to treat a given object as a scalar in a broadcast expression. Perhaps the most common practice is to use
Ref
, but this is arguably not the intended use ofRef
and can be less performant thantuple
becauseRef
(currently) blocks constant propagation. For a contrived example:This difference is perhaps inconsequential currently, but in a future where Julia is better able to reason about non-mutated arrays it could become more important, so I think it's better to recommend a more future-proof style. Of course, one could say that in that future
Ref
might also play better with constant prop, but I think that may be undesirable becauseRef
is often used for explicitly blocking constant prop, i.e. in benchmarking.I also modified an example that wrote
ceil.((UInt8,), [1.2 3.4; 5.6 6.7])
to instead sayceil.(UInt8, [1.2 3.4; 5.6 6.7])
because there is no reason to wrapUInt8
in a tuple since it is already a broadcast scalar.