Update type-cache after each user interaction#1014
Conversation
|
Very cool 🎉 Slightly offtopic, but would it be possible to put that expression in a separate function? I was thinking that then IJulia could call it directly instead of reimplementing it: https://github.com/JuliaLang/IJulia.jl/blob/8e17d35ec29c89a7b51b75fd649021a94c647c3c/ext/IJuliaReviseExt.jl#L18 |
|
In my test case from #988 (comment) , populating the type cache takes 20s every time, even after the cache has already been populated and no changes to any types have been made. Hence, I'm not sure if this extra background work is worth it. Perhaps we can limit the invocation of |
|
It only takes 20s because you've just loaded a ton of packages. Once they are "processed" it will be ~15ms. You can try this manually: after loading your package with all its dependencies, call julia> @time Revise.foreach_subtype(Any) do @nospecialize type
Revise.fieldtypes_cached(type)
endand you should see it take ~20s. Then do the exact same thing again in this same session (without loading a whole bunch of new packages), and it should be very fast. In other words, the way this fixes the slow-revision problem is that it starts scanning loaded packages in a background thread as soon as they load. That process should be finished (presuming it will take the user some to make some edits in their editor) before the next type revision occurs and the user will experience little latency. |
|
20s hogging a thread just by loading packages seems a bit much to enable by default 👀 |
831397b to
40e8e4f
Compare
Done |
Fair enough. It's a pretty big package, though (not quite "OmniPackage" but getting there), and just loading it is also on par with that amount of time. Not sure what to do about the default. I might leave it on a bit and play with it myself for a while. I still haven't abandoned 1.10 for real work but I think now is the time... |
I don't find this to be true in my tests. For testing purposes, I've modified the invocation of return Expr(:toplevel,
:($isempty($revision_queue) || $(Base.invokelatest)($revise)),
quote
let $result = $ex
# Base.Threads.@spawn :default $(repopulate_typecache)()
t = time()
println("Repopulating type cache...")
$repopulate_typecache()
println("Repopulation done after ", round(time() - t, sigdigits=3), " seconds.")
$result
end
end
)With those changes, here's what I get with the test package from #988 (comment) ❯ JULIA_LOAD_PATH="@." julia --startup-file=no
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.12.5 (2026-02-09)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org release
|__/ |
julia> using Revise
julia> @time Revise.foreach_subtype(Any) do @nospecialize type
Revise.fieldtypes_cached(type)
end
0.577651 seconds (245.60 k allocations: 176.847 MiB, 2.10% gc time, 2.21% compilation time)
Repopulating type cache...
Repopulation done after 0.57 seconds.
julia> using Foo
Repopulating type cache...
Repopulation done after 12.4 seconds.
julia> Foo.Bar()
Repopulating type cache...
Repopulation done after 12.6 seconds.
Foo.Bar(4.0, 5)
julia> Foo.Bar()
Repopulating type cache...
Repopulation done after 12.5 seconds.
Foo.Bar(4.0, 5) |
|
Similarly: Revise.jl-struct-revision-kwdef on main [!?] is 📦 v0.1.0 via ஃ v1.12.5 took 2m6s
❯ JULIA_LOAD_PATH="@." julia --startup-file=no
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.12.5 (2026-02-09)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org release
|__/ |
julia> using Revise
julia> @time Revise.foreach_subtype(Any) do @nospecialize type
Revise.fieldtypes_cached(type)
end
0.563205 seconds (245.60 k allocations: 176.633 MiB, 2.12% gc time, 2.26% compilation time)
Repopulating type cache...
Repopulation done after 0.582 seconds.
julia> @time Revise.foreach_subtype(Any) do @nospecialize type
Revise.fieldtypes_cached(type)
end
0.560553 seconds (236.09 k allocations: 176.144 MiB, 2.12% gc time, 1.86% compilation time)
Repopulating type cache...
Repopulation done after 0.556 seconds.
julia> using Foo
Repopulating type cache...
Repopulation done after 12.6 seconds.
julia> @time Revise.foreach_subtype(Any) do @nospecialize type
Revise.fieldtypes_cached(type)
end
12.659920 seconds (3.93 M allocations: 3.295 GiB, 3.33% gc time, 0.07% compilation time)
Repopulating type cache...
Repopulation done after 12.7 seconds.
julia> @time Revise.foreach_subtype(Any) do @nospecialize type
Revise.fieldtypes_cached(type)
end
12.824380 seconds (3.93 M allocations: 3.295 GiB, 4.56% gc time, 0.08% compilation time)
Repopulating type cache...
Repopulation done after 12.6 seconds.Note that I did not make any modifications to source code in any of these tests. |
|
Ah shoot, I must have benchmarked on a small system. (Or maybe on julia master?) Just the call to OK, I agree we probably have to turn this off by default, at least until JuliaLang/julia#60736 can be backported. |
The intent here is to keep the type-cache up to date, so that if and when a type is redefined, there isn't a large latency before the revision can start. This is the last piece of the puzzle needed to Fix #988
40e8e4f to
7614d63
Compare
|
Note to anyone who might be tempted to merge this: it's not really feasible at present, need to wait for more performance work on the Julia side. |
The intent here is to keep the type-cache up to date, so that if and when a type is redefined, there isn't a large latency before the revision can start.
This is the last piece of the puzzle needed to
Fix #988
Note this also re-enables type revision by default. CC @lassepe, @JamesWrigley . If you haven't followed, #1013 yielded a dramatic speedup. This PR should keep the cache up-to-date after each REPL command, which I hope will mean that it will be quite rare to hit long latencies on type-revision.