-
Notifications
You must be signed in to change notification settings - Fork 52
Use changes to Julia to have @snoopi report _all MethodInstances_ that are inferred during a single inference call
#138
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
Conversation
|
I'm quite sure that I haven't gone about this in the best way. I'd love any suggestions or help if you are interested!!!! :) ❤️ ! Hopefully this will prove useful for others as well! |
```julia
julia> module M
i(x) = x+3
i2(x) = x+2
h(a::Array) = i2(a[1]::Integer) + i(a[1]::Integer) + 2
g(y::Integer, x) = h(Any[y]) + Int(x)
end;
WARNING: replacing module M.
julia> SnoopCompileCore.@Snoopi begin
M.g(2,3.0)
end
3-element Vector{Tuple{Float64,Core.MethodInstance,Vector{Pair{Core.MethodInstance,Core.MethodInstance}}}}:
(0.00024819374084472656, MethodInstance for i(::Int64), [])
(0.0003299713134765625, MethodInstance for i2(::Int64), [])
(0.001934051513671875, MethodInstance for g(::Int64, ::Float64), [MethodInstance for g(::Int64, ::Float64) => MethodInstance for h(::Vector{Any}), MethodInstance for h(::Vector{Any}) => MethodInstance for i2(::Integer), MethodInstance for h(::Vector{Any}) => MethodInstance for i(::Integer)])
```
…erly verbose)
I _think_ this now does what we want?
```julia
julia> module M
i(x) = x+5
i2(x) = x+2
h(a::Array) = i2(a[1]::Integer) + i(a[1]::Integer) + 2
g(y::Integer, x) = h(Any[y]) + Int(x)
end;
WARNING: replacing module M.
julia> out = SnoopCompileCore.@Snoopi begin
M.g(2,3.0)
end
3-element Vector{Tuple{Float64,Core.MethodInstance,Vector{Pair{Core.MethodInstance,Core.MethodInstance}}}}:
(0.00022411346435546875, MethodInstance for i(::Int64), [])
(0.00024890899658203125, MethodInstance for i2(::Int64), [])
(0.0016169548034667969, MethodInstance for g(::Int64, ::Float64), [MethodInstance for g(::Int64, ::Float64) => MethodInstance for h(::Vector{Any}), MethodInstance for h(::Vector{Any}) => MethodInstance for i2(::Integer), MethodInstance for h(::Vector{Any}) => MethodInstance for i(::Integer)])
julia> SnoopCompile.collect_per_method_inference_timings(out, :(module M
i(x) = x+5
i2(x) = x+2
h(a::Array) = i2(a[1]::Integer) + i(a[1]::Integer) + 2
g(y::Integer, x) = h(Any[y]) + Int(x)
end))
Launching new julia process to run commands...
done.
[ Info: MethodInstance for i(::Int64): total: 0.00022411346435546875 : 0.034790221
Dict{Any,Float64} with 1 entry:
Tuple{typeof(i),Int64} => 0.0347902
Launching new julia process to run commands...
done.
[ Info: MethodInstance for i2(::Int64): total: 0.00024890899658203125 : 0.035098683
Dict{Any,Float64} with 1 entry:
Tuple{typeof(i2),Int64} => 0.0350987
Launching new julia process to run commands...
done.
[ Info: MethodInstance for g(::Int64, ::Float64): total: 0.0016169548034667969 : 0.046937507
Dict{Any,Float64} with 4 entries:
Tuple{typeof(i2),Integer} => 0.0110619
Tuple{typeof(h),Vector{Any}} => 0.025103
Tuple{typeof(i),Integer} => 0.00563928
Tuple{typeof(g),Int64,Float64} => 0.00513329
[ Info: Returning merged results:
Dict{Any,Float64} with 6 entries:
Tuple{typeof(i2),Integer} => 0.0110619
Tuple{typeof(h),Vector{Any}} => 0.025103
Tuple{typeof(i),Integer} => 0.00563928
Tuple{typeof(i2),Int64} => 0.0350987
Tuple{typeof(g),Int64,Float64} => 0.00513329
Tuple{typeof(i),Int64} => 0.0347902
julia>
```
Now working through some more isssues, like not missing edges and deduplicating edges and nodes.
- for more accurate timings, and - prevent stackoverflow
… of the graph each time?
|
Awesome that you're doing this. If I don't notice when you turn this into a real PR, just ping me. |
|
Thank you @timholy!! After some more thinking, together with @Sacha0, i think we think that the approach we'd taken is for is probably not the best way to go about measuring this. Our next idea is to try a bit more like the other approaches, and just find a better place to inject the timings. But then we realized if we were just able to report the timings in a nested structure, like a profile, they'd make sense! And then i remembered this is exactly what https://github.com/KristofferC/TimerOutputs.jl already does! 😁 So i think either duplicating that, or literally just using that, would get us what we want! As a first quick test, I just slapped TimerOutputs inside the compiler, and in this small test it seems indeed reasonable: julia> module M
i(x) = x+5
i2(x) = x+2
h(a::Array) = i2(a[1]::Integer) + i(a[1]::Integer) + 2
g(y::Integer, x) = h(Any[y]) + Int(x)
end;
WARNING: replacing module M.
julia> _to_ = TimerOutputs.TimerOutput();
julia> @code_typed M.g(2,3); # Measure inference timee
julia> _to_
─────────────────────────────────────────────────────────────────────────────────────────────────
Time Allocations
────────────────────── ───────────────────────
Tot / % measured: 9.65s / 0.02% 887KiB / 30.7%
Section ncalls time %tot avg alloc %tot avg
─────────────────────────────────────────────────────────────────────────────────────────────────
MethodInstance for g(::Int64, ::Int64) 1 2.16ms 100% 2.16ms 273KiB 100% 273KiB
MethodInstance for h(::Vector{Any}) 1 766μs 35.5% 766μs 187KiB 68.7% 187KiB
MethodInstance for i2(::Integer) 1 236μs 10.9% 236μs 42.6KiB 15.6% 42.6KiB
MethodInstance for i(::Integer) 1 178μs 8.23% 178μs 42.6KiB 15.6% 42.6KiB
─────────────────────────────────────────────────────────────────────────────────────────────────So i'll give an approach like that a go, next!! 😁 |
|
(I've filed an issue in TimerOutputs.jl to see about using it to generate essentially profiles, like i want above 😊: |
|
(fwiw, here's the git diff for what I was demoing, above. I'm trying it now on our codebase to see if it reports anything useful!): diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl
index a0abd97276..a8bec821b5 100644
--- a/base/compiler/typeinfer.jl
+++ b/base/compiler/typeinfer.jl
@@ -9,6 +9,11 @@ function typeinf(interp::AbstractInterpreter, result::InferenceResult, cached::B
end
function typeinf(interp::AbstractInterpreter, frame::InferenceState)
+ Main.TimerOutputs.@timeit Main._to_ Main.string(frame.linfo) begin
+ _typeinf(interp, frame)
+ end
+end
+function _typeinf(interp::AbstractInterpreter, frame::InferenceState)
typeinf_nocycle(interp, frame) || return false # frame is now part of a higher cycle
# with no active ip's, frame is done
frames = frame.callers_in_cycle(The above of course wouldn't compile, but i'm eval'ing it in after into a running session. If this approach works, i'll likely copy what you did for |
|
Cool. Not sure if you know this but |
|
Thanks @timholy -- I didn't know that, and I appreciate the tip! 😁 |
Builds on JuliaLang/julia#37535 to expose this information through SnoopCompileCore