-
-
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
Refactor null_safe_op to workaround codegen changes #21290
Conversation
cc @nalimilan |
edd906b
to
4054143
Compare
@nanosoldier Edit: Hm, maybe that's not the right way to invoke the nullable benchmarks... |
base/broadcast.jl
Outdated
function null_safe_eltype_op(op, xs...) | ||
Base.@_pure_meta | ||
null_safe_op(op, typeeltypestuple(xs...)) | ||
end |
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.
eltype(a)
is not pure, so it's likely a bad idea to mark this entire stack of calls as @pure
(it seems the same applies to eltypestuple
also).
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.
I'm cautiously optimistic that simply removing Base.@_pure_meta
is OK, and inference can infer everything anyway. So far I haven't noticed any inference regressions and make test
still passes. Could you/someone else run nanosoldier to make sure nothing regresses?
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.
Why is eltype(a)
not pure? Is anything more complex than f() = 1
pure? Or is that also not pure?
@nanosoldier |
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @jrevels |
None of the regressions/improvements make any sense 😕. But a lot of the regressions are pretty substantial. (The improvements are probably noise.) |
Interestingly, the nullable benchmarks seem to be unaffected. |
The dot calls are probably not benchmarked. We should add benchmarks for them. |
If you mean |
It should be, but Nanosoldier won't catch it if you edit your submission after submitting it. Let's try with a fresh submission: @nanosoldier |
Your benchmark job has completed - no performance regressions were detected. A full report can be found here. cc @jrevels |
Could you give an example of the codegen regression --- e.g. a particular |
@JeffBezanson Sure, see #21305. I'm currently bisecting to see if I can find the commit that changed this. |
@TotalVerb Hopefully this PR is not needed any more --- can you double check? |
@JeffBezanson #21310 is a nice change but I don't think it's the particular problem here. The file from #21305 still has long LLVM: eltypestuple() = Tuple{}
eltypestuple(x, xs...) = Tuple{eltype(x), eltypes(xs...).types...}
null_safe_op(::Any, ::Type, ::Type...) = false
Base.@pure null_safe_eltype_op(f, xs...) =
null_safe_op(f, eltypestuple(xs...).types...)
@inline function broadcast_c(f, a...)
if null_safe_eltype_op(f, a...)
0
else
1
end
end
f() = broadcast_c(1:10, 1:10, 1:10)
code_llvm(STDOUT, f, Tuple{}) The return value is correctly inferred as a constant, but the issue is that there is no way it seems for inference to figure out that |
The Variables:
#self#::#f
Body:
begin
$(Expr(:inbounds, false))
# meta: location /tmp/regression.jl broadcast_c 9
(Core._apply)(Main.null_safe_op, (Core.tuple)($(Expr(:new, UnitRange{Int64}, 1, :((Base.select_value)((Base.sle_int)(1, 10)::Bool, 10, (Base.sub_int)(1, 1)::Int64)::Int64))))::Tuple{UnitRange{Int64}}, (Core.getfield)((Core._apply)(Core.apply_type, (Core.tuple)(Main.Tuple, Int64)::Tuple{DataType,DataType}, (Core.getfield)((Main.eltypes)($(Expr(:new, UnitRange{Int64}, 1, :((Base.select_value)((Base.sle_int)(1, 10)::Bool, 10, (Base.sub_int)(1, 1)::Int64)::Int64))))::ANY, :types)::ANY)::ANY, :types)::ANY)::Bool
goto 6 # line 10:
6:
# meta: pop location
$(Expr(:inbounds, :pop))
return 1
end::Int64 |
In that code, |
My bad! You're right, this problem has been fixed. |
There have been codegen / inference changes since #16961 was merged that result in substantial (~40x) regressions in trivial
Nullable
broadcasts.See https://discourse.julialang.org/t/performance-of-dot-operator-on-nullables/3071 for a report of the regressions.
This PR works around those changes by combining all parameters for
null_safe_op
into a singleType{<:Tuple}
argument, which avoids the splat of(::Type{<:Tuple}).parameters
which had previously confounded codegen (and would not be eliminated even when the function is declared@pure
.)Since this is a performance only change it should go into 0.6.