Skip to content
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

Missing elements in invalidation log #58080

Open
timholy opened this issue Apr 11, 2025 · 3 comments
Open

Missing elements in invalidation log #58080

timholy opened this issue Apr 11, 2025 · 3 comments
Milestone

Comments

@timholy
Copy link
Member

timholy commented Apr 11, 2025

#57934 had a side effect of excluding some elements from the invalidation logs processed by SnoopCompile. Here's a MWE. The distinguishing characteristic is the presence of both a poorly-specialized and invoked caller with the same signature:

f(::Integer) = 1
callsfrts(x) = f(Base.inferencebarrier(x)::Signed)
invokesfs(x) = invoke(f, Tuple{Signed}, x)

# compilation
invokesfs(1)                        # invoked callee
callsfrts(1)                        # runtime-dispatched callee

# invalidation
logmeths = ccall(:jl_debug_method_invalidation, Any, (Cint,), 1);
f(::Int) = 2
f(::Signed) = 4
ccall(:jl_debug_method_invalidation, Any, (Cint,), 0);

m = which(f, (Signed,))
m  logmeths

You want the final line to be true (and it was before #57934).

Here's the backedge graph before invalidation:

julia> m = only(methods(f))
f(::Integer)
     @ Main REPL[1]:1

julia> mis = collect(Base.specializations(m))
2-element Vector{Any}:
 MethodInstance for f(::Int64)
 MethodInstance for f(::Signed)

julia> mi = mis[1]
MethodInstance for f(::Int64)

julia> mi.backedges
2-element Vector{Any}:
 Tuple{typeof(f), Signed}
 CodeInstance for MethodInstance for invokesfs(::Int64)

julia> mi2 = mis[2]
MethodInstance for f(::Signed)

julia> mi2.backedges
1-element Vector{Any}:
 CodeInstance for MethodInstance for callsfrts(::Int64)
@Keno
Copy link
Member

Keno commented Apr 11, 2025

You want the final line to be true (and it was before #57934).

Why? That method is the newest method added, so by definition none of its code instances were invalidated.

@timholy
Copy link
Member Author

timholy commented Apr 12, 2025

The log is broken into sections, and each section terminates with the new method that triggered the preceding set of invalidations. That way SnoopCompile can identify the "culprit."

@timholy
Copy link
Member Author

timholy commented Apr 12, 2025

BTW that demo works on Julia 1.10 too. On 1.10:

julia> logmeths
14-element Vector{Any}:
  MethodInstance for callsfrts(::Int64)
 1
  MethodInstance for f(::Signed)
  "jl_method_table_insert"
  f(::Int64) @ Main REPL[7]:1                                  # method responsible for the first section
  "jl_method_table_insert"
  MethodInstance for invokesfs(::Int64)
 1
  MethodInstance for f(::Int64)
  "jl_method_table_insert"
  MethodInstance for f(::Signed)
  "jl_method_table_insert"
  f(::Signed) @ Main REPL[8]:1                                 # method responsible for the second section
  "jl_method_table_insert"

vs master:

julia> logmeths
10-element Vector{Any}:
  MethodInstance for callsfrts(::Int64)
 1
  MethodInstance for f(::Signed)
  "jl_method_table_insert"
  f(::Int64) @ Main REPL[7]:1                                  # method responsible for the first section
  "jl_method_table_insert"
  MethodInstance for invokesfs(::Int64)                        # a second section starts, but no causal method is logged
 1
  MethodInstance for f(::Int64)
  "jl_method_table_insert"

And here's what this demo looks like on 1.10 with SnoopCompile:

julia> using SnoopCompileCore

julia> invs = @snoop_invalidations begin
       f(::Integer) = 1
       callsfrts(x) = f(Base.inferencebarrier(x)::Signed)
       invokesfs(x) = invoke(f, Tuple{Signed}, x)

       # compilation
       invokesfs(1)                        # invoked callee
       callsfrts(1)

       f(::Int) = 2
       f(::Signed) = 4
       end
14-element Vector{Any}:
  MethodInstance for callsfrts(::Int64)
 1
  MethodInstance for f(::Signed)
  "jl_method_table_insert"
  f(::Int64) @ Main REPL[2]:10
  "jl_method_table_insert"
  MethodInstance for invokesfs(::Int64)
 1
  MethodInstance for f(::Int64)
  "jl_method_table_insert"
  MethodInstance for f(::Signed)
  "jl_method_table_insert"
  f(::Signed) @ Main REPL[2]:11
  "jl_method_table_insert"

julia> using SnoopCompile

julia> trees = invalidation_trees(invs)
2-element Vector{SnoopCompile.MethodInvalidations}:
 inserting f(::Int64) @ Main REPL[2]:10 invalidated:
   backedges: 1: superseding f(::Integer) @ Main REPL[2]:2 with MethodInstance for f(::Signed) (1 children)

 inserting f(::Signed) @ Main REPL[2]:11 invalidated:
   backedges: 1: superseding f(::Integer) @ Main REPL[2]:2 with MethodInstance for f(::Int64) (1 children)


julia> using SnoopCompile.AbstractTrees

julia> print_tree(only(trees[1].backedges))
MethodInstance for f(::Signed) at depth 0 with 1 children
└─ MethodInstance for callsfrts(::Int64) at depth 1 with 0 children

julia> print_tree(only(trees[2].backedges))
MethodInstance for f(::Int64) at depth 0 with 1 children
└─ MethodInstance for invokesfs(::Int64) at depth 1 with 0 children

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants