broadcast: compute the result eltype via inference directly (#62564)
rebase of https://github.com/JuliaLang/julia/pull/39295, hence
@nalimilan I've listed you as a coauthor; I hope that's ok. the
arguments should be mostly the same as that PR, which seemed to broadly
have support it just stalled.
the differences in the rebase:
* I added the `convert(eltype(A), A[i])::eltype(A)` to fix some observed
issue relating to the fact that `getindex(::SymTridiagonal, _)` can
return a `Transpose`
* I use `eltype(eachindex(bc))` rather than `Int` in attempt to resolve
https://github.com/JuliaLang/julia/pull/39295#discussion_r561198878
arguments for:
* better inference any time we have fused broadcast with internal
instability, which can lead to dramatic runtime performance
improvements. e.g. consider
```julia
f(c, x, y) = sqrt.(ifelse.(c, x, y))
Base.return_types(f, Tuple{Vector{Bool}, Vector{Int}, Vector{Float64}})[1]
Union{Vector{Any}, Vector{Float64}} # master
Vector{Float64} # PR
```
* this also improves consistency of the type of empty arrays. using the
example above, `f(Bool[], Int[], Float64[])` gives `Any[]` on master but
`Float64[]` on PR
* gets to take advantage of all the intelligence in inference, and is a
more unified code path, rather than handrolling what is essentially its
own janky inference algorithm
* fixes https://github.com/JuliaLang/julia/issues/31890
and against:
* `combine_eltypes` is a popular internal in packages. it's not public
so we can delete it, but empirically some stuff will break (including
`SparseArrays.jl`)
* though usually better, inference is occasionally worse than
`combine_eltypes` was, e.g. in cases when there is pathological
recursion. I suppose these kinds of situations might be fixed by
something like https://github.com/JuliaLang/julia/pull/48059
```julia
struct SqDev; c::Vector{Float64}; end
(s::SqDev)(x) = sum((x .- s.c) .^ 2)
struct Outer; s::SqDev; end
(o::Outer)(v) = sum(o.s.(v .* 2.0) .+ 1.0)
k(o, grids) = o.(grids)
Base.return_types(k, Tuple{Outer, Vector{Vector{Float64}}})[1]
Vector{Float64} # master
Union{BitVector, Vector} # PR
```
* inference seems to be on average (though highly noisy estimate) about
~5% slower than `combine_eltypes`, contributing to TTFB. however the
time for `test/broadcast.jl` appears to be unchanged.
---------
Co-authored-by: Milan Bouchet-Valat <nalimilan@club.fr>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: OpenAI Codex <codex@openai.com>