JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.63k stars 5.48k forks source link

`--trace-compile` incorrectly reports only-inferred methods as being re-compiled #56155

Open topolarity opened 1 week ago

topolarity commented 1 week ago

There are times (such as when inlining) when Julia will create a CodeInstance that has no code associated with it.

For example:

using Base.Filesystem: contractuser
mi = methods(contractuser, (String,))[1].specializations::Base.MethodInstance

println("world range: ", (mi.cache.min_world, mi.cache.max_world))
println("invoke, specptr: ", (mi.cache.invoke, mi.cache.specptr))
println("next?: ", (isdefined(mi.cache, :next)))

println(contractuser("")) # trigger compilation

println("world range: ", (mi.cache.min_world, mi.cache.max_world))
println("invoke, specptr: ", (mi.cache.invoke, mi.cache.specptr))
println("next?: ", (isdefined(mi.cache, :next)))

This outputs:

world range: (0x0000000000003f84, 0xffffffffffffffff)
invoke, specptr: (Ptr{Nothing}(0x0000000000000000), Ptr{Nothing}(0x0000000000000000))
next?: false

world range: (0x0000000000003f84, 0xffffffffffffffff)
invoke, specptr: (Ptr{Nothing}(0x00007f6f10b09680), Ptr{Nothing}(0x00007f6f10b09590))
next?: false

which shows that contractuser(::String) was never invalidated (and in fact only has one CodeInstance). It just never had any code associated with it because it was only inferred and then inlined.

However if you run with --trace-compile=compile.log you'll see:

...
precompile(Tuple{typeof(Base.Filesystem.contractuser), String}) # recompile
...

You can see we reported precompile(Tuple{typeof(Base.Filesystem.contractuser), String}) # recompile

topolarity commented 1 week ago

This also affects the "% re-compilation" metric reported in @timed etc.

vtjnash commented 5 days ago

Yes, this is an implementation issue that "recompiled" is basically meaningless right now, since is_recompile never actually checks if it was compiled before (invoke is set)