timholy / SnoopCompile.jl

Provide insights about latency (TTFX) for Julia packages
https://timholy.github.io/SnoopCompile.jl/dev/
Other
309 stars 48 forks source link

Precompilation fails using `SnoopPrecompile` in `Plots` #295

Closed t-bltg closed 2 years ago

t-bltg commented 2 years ago

@timholy, I am trying to rewrite the pre-compilation process for Plots.jl using SnoopPrecompile and @precompile_setup and @precompile_all_calls (after https://github.com/JuliaPlots/Plots.jl/issues/4079#issuecomment-1047716834).

Here is my attempt:

src/Plots.jl

# include("precompile_includer.jl")  # old precompilation statements

@precompile_setup begin
    fn = tempname()
    n = length(_examples)
    lines = sizehint!(String["gr()"], 10n)
    for i in setdiff(1:n, _backend_skips[:gr])
        append!(lines, string.(_examples[i].exprs))
        push!(lines, "gui()")
    end
    write(fn, join(lines, '\n'))
    # run (`cat $fn`)
    @precompile_all_calls begin
        include(fn)
    end
end

Running $ julia -e 'using Plots', fails with:

┌ Error: Error importing GR_jll:
│   exception =
│    Evaluation into the closed module `GR` breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating `GR` with `eval` during precompilation - don't do this.
│    Stacktrace:
│      [1] eval
│        @ ./boot.jl:368 [inlined]
│      [2] load_libs(always::Bool)
│        @ GR [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:32
│      [3] get_func_ptr
│        @ [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:73 [inlined]
│      [4] get_func_ptr
│        @ [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:72 [inlined]
│      [5] libGR_ptr
│        @ [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:83 [inlined]
│      [6] setcharheight(height::Float64)
│        @ GR [...]/.julia/packages/GR/uG7UO/src/GR.jl:1668
│      [7] gr_set_font(f::Plots.Font, s::Plots.Subplot{Plots.GRBackend}; halign::Symbol, valign::Symbol, color::ColorTypes.RGB{FixedPointNumbers.N0f8}, rotation::Int64)
│        @ Plots [...]/Plots.jl/src/backends/gr.jl:437
│      [8] gr_set_tickfont(sp::Plots.Subplot{Plots.GRBackend}, letter::Symbol)
│        @ Plots [...]/Plots.jl/src/backends/gr.jl:707
│      [9] _update_min_padding!(sp::Plots.Subplot{Plots.GRBackend})
│        @ Plots [...]/Plots.jl/src/backends/gr.jl:919
│     [10] iterate
│        @ ./generator.jl:47 [inlined]
│     [11] _collect(c::Matrix{RecipesBase.AbstractLayout}, itr::Base.Generator{Matrix{RecipesBase.AbstractLayout}, typeof(Plots._update_min_padding!)}, #unused#::Base.EltypeUnknown, isz::Base.HasShape{2})
│        @ Base ./array.jl:807
│     [12] collect_similar
│        @ ./array.jl:716 [inlined]
│     [13] map
│        @ ./abstractarray.jl:2933 [inlined]
│     [14] _update_min_padding!(layout::Plots.GridLayout)
│        @ Plots [...]/Plots.jl/src/layouts.jl:287
│     [15] prepare_output(plt::Plots.Plot{Plots.GRBackend})
│        @ Plots [...]/Plots.jl/src/plot.jl:232
│     [16] display
│        @ [...]/Plots.jl/src/output.jl:163 [inlined]
│     [17] gui(plt::Plots.Plot{Plots.GRBackend}) (repeats 2 times)
│        @ Plots [...]/Plots.jl/src/output.jl:153
│     [18] top-level scope
│        @ /tmp/jl_23C767cXUD:6
│     [19] include(mod::Module, _path::String)
│        @ Base ./Base.jl:419
│     [20] include(x::String)
│        @ Plots [...]/Plots.jl/src/Plots.jl:1
│     [21] macro expansion
│        @ [...]/Plots.jl/src/Plots.jl:275 [inlined]
│     [22] macro expansion
│        @ [...]/.julia/packages/SnoopPrecompile/UWvXF/src/SnoopPrecompile.jl:51 [inlined]
│     [23] macro expansion
│        @ [...]/Plots.jl/src/Plots.jl:274 [inlined]
│     [24] top-level scope
│        @ [...]/.julia/packages/SnoopPrecompile/UWvXF/src/SnoopPrecompile.jl:107 [inlined]
│     [25] top-level scope
│        @ [...]/Plots.jl/src/Plots.jl:0
│     [26] include
│        @ ./Base.jl:419 [inlined]
│     [27] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::Nothing)
│        @ Base ./loading.jl:1554
│     [28] top-level scope
│        @ stdin:1
│     [29] eval
│        @ ./boot.jl:368 [inlined]
│     [30] include_string(mapexpr::typeof(identity), mod::Module, code::String, filename::String)
│        @ Base ./loading.jl:1428
│     [31] include_string(m::Module, txt::String, fname::String)
│        @ Base ./loading.jl:1438
│     [32] exec_options(opts::Base.JLOptions)
│        @ Base ./client.jl:301
│     [33] _start()
│        @ Base ./client.jl:522
└ @ GR [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:38
Your GR installation is incomplete. Rerunning build step for GR package.
┌ Info: Switching provider to GR due to error in depsfile
└   depsfile = "[...]/.julia/packages/GR/uG7UO/deps/deps.jl"
┌ Warning: GR attempted to rebuild but failed due to ErrorException("Evaluation into the closed module `GR` breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating `GR` with `eval` during precompilation - don't do this."). Setting ENV["JULIA_GR_REBUILD"] = false.
└ @ GR [...]/.julia/packages/GR/uG7UO/src/GR.jl:375
ERROR: LoadError: GR was not built correctly and could not be automatically rebuilt.
get(ENV, "JULIA_GR_REBUILD", "true") == false
[...]/.julia/packages/GR/uG7UO/src/../deps/gr is not a directory.
GR_jll could not be loaded.

Run the following commands:

ENV["GRDIR"] = ""
using Pkg; Pkg.build("GR")

Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:35
  [2] __init__()
    @ GR [...]/.julia/packages/GR/uG7UO/src/GR.jl:380
  [3] load_libs(always::Bool)
    @ GR [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:41
  [4] get_func_ptr
    @ [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:73 [inlined]
  [5] get_func_ptr
    @ [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:72 [inlined]
  [6] libGR_ptr
    @ [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:83 [inlined]
  [7] setcharheight(height::Float64)
    @ GR [...]/.julia/packages/GR/uG7UO/src/GR.jl:1668
  [8] gr_set_font(f::Plots.Font, s::Plots.Subplot{Plots.GRBackend}; halign::Symbol, valign::Symbol, color::ColorTypes.RGB{FixedPointNumbers.N0f8}, rotation::Int64)
    @ Plots [...]/Plots.jl/src/backends/gr.jl:437
  [9] gr_set_tickfont(sp::Plots.Subplot{Plots.GRBackend}, letter::Symbol)
    @ Plots [...]/Plots.jl/src/backends/gr.jl:707
 [10] _update_min_padding!(sp::Plots.Subplot{Plots.GRBackend})
    @ Plots [...]/Plots.jl/src/backends/gr.jl:919
 [11] iterate
    @ ./generator.jl:47 [inlined]
 [12] _collect(c::Matrix{RecipesBase.AbstractLayout}, itr::Base.Generator{Matrix{RecipesBase.AbstractLayout}, typeof(Plots._update_min_padding!)}, #unused#::Base.EltypeUnknown, isz::Base.HasShape{2})
    @ Base ./array.jl:807
 [13] collect_similar
    @ ./array.jl:716 [inlined]
 [14] map
    @ ./abstractarray.jl:2933 [inlined]
 [15] _update_min_padding!(layout::Plots.GridLayout)
    @ Plots [...]/Plots.jl/src/layouts.jl:287
 [16] prepare_output(plt::Plots.Plot{Plots.GRBackend})
    @ Plots [...]/Plots.jl/src/plot.jl:232
 [17] display
    @ [...]/Plots.jl/src/output.jl:163 [inlined]
 [18] gui(plt::Plots.Plot{Plots.GRBackend}) (repeats 2 times)
    @ Plots [...]/Plots.jl/src/output.jl:153
 [19] top-level scope
    @ /tmp/jl_23C767cXUD:6
 [20] include(mod::Module, _path::String)
    @ Base ./Base.jl:419
 [21] include(x::String)
    @ Plots [...]/Plots.jl/src/Plots.jl:1
 [22] macro expansion
    @ [...]/Plots.jl/src/Plots.jl:275 [inlined]
 [23] macro expansion
    @ [...]/.julia/packages/SnoopPrecompile/UWvXF/src/SnoopPrecompile.jl:51 [inlined]
 [24] macro expansion
    @ [...]/Plots.jl/src/Plots.jl:274 [inlined]
 [25] top-level scope
    @ [...]/.julia/packages/SnoopPrecompile/UWvXF/src/SnoopPrecompile.jl:107 [inlined]
 [26] top-level scope
    @ [...]/Plots.jl/src/Plots.jl:0
 [27] include
    @ ./Base.jl:419 [inlined]
 [28] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::Nothing)
    @ Base ./loading.jl:1554
 [29] top-level scope
    @ stdin:1
in expression starting at /tmp/jl_23C767cXUD:6
in expression starting at [...]/Plots.jl/src/Plots.jl:1
in expression starting at stdin:1

caused by: Evaluation into the closed module `GR` breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating `GR` with `eval` during precompilation - don't do this.
Stacktrace:
  [1] eval
    @ ./boot.jl:368 [inlined]
  [2] load_libs(always::Bool)
    @ GR [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:32
  [3] get_func_ptr
    @ [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:73 [inlined]
  [4] get_func_ptr
    @ [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:72 [inlined]
  [5] libGR_ptr
    @ [...]/.julia/packages/GR/uG7UO/src/funcptrs.jl:83 [inlined]
  [6] setcharheight(height::Float64)
    @ GR [...]/.julia/packages/GR/uG7UO/src/GR.jl:1668
  [7] gr_set_font(f::Plots.Font, s::Plots.Subplot{Plots.GRBackend}; halign::Symbol, valign::Symbol, color::ColorTypes.RGB{FixedPointNumbers.N0f8}, rotation::Int64)
    @ Plots [...]/Plots.jl/src/backends/gr.jl:437
  [8] gr_set_tickfont(sp::Plots.Subplot{Plots.GRBackend}, letter::Symbol)
    @ Plots [...]/Plots.jl/src/backends/gr.jl:707
  [9] _update_min_padding!(sp::Plots.Subplot{Plots.GRBackend})
    @ Plots [...]/Plots.jl/src/backends/gr.jl:919
 [10] iterate
    @ ./generator.jl:47 [inlined]
 [11] _collect(c::Matrix{RecipesBase.AbstractLayout}, itr::Base.Generator{Matrix{RecipesBase.AbstractLayout}, typeof(Plots._update_min_padding!)}, #unused#::Base.EltypeUnknown, isz::Base.HasShape{2})
    @ Base ./array.jl:807
 [12] collect_similar
    @ ./array.jl:716 [inlined]
 [13] map
    @ ./abstractarray.jl:2933 [inlined]
 [14] _update_min_padding!(layout::Plots.GridLayout)
    @ Plots [...]/Plots.jl/src/layouts.jl:287
 [15] prepare_output(plt::Plots.Plot{Plots.GRBackend})
    @ Plots [...]/Plots.jl/src/plot.jl:232
 [16] display
    @ [...]/Plots.jl/src/output.jl:163 [inlined]
 [17] gui(plt::Plots.Plot{Plots.GRBackend}) (repeats 2 times)
    @ Plots [...]/Plots.jl/src/output.jl:153
 [18] top-level scope
    @ /tmp/jl_23C767cXUD:6
 [19] include(mod::Module, _path::String)
    @ Base ./Base.jl:419
 [20] include(x::String)
    @ Plots [...]/Plots.jl/src/Plots.jl:1
 [21] macro expansion
    @ [...]/Plots.jl/src/Plots.jl:275 [inlined]
 [22] macro expansion
    @ [...]/.julia/packages/SnoopPrecompile/UWvXF/src/SnoopPrecompile.jl:51 [inlined]
 [23] macro expansion
    @ [...]/Plots.jl/src/Plots.jl:274 [inlined]
 [24] top-level scope
    @ [...]/.julia/packages/SnoopPrecompile/UWvXF/src/SnoopPrecompile.jl:107 [inlined]
 [25] top-level scope
    @ [...]/Plots.jl/src/Plots.jl:0
 [26] include
    @ ./Base.jl:419 [inlined]
 [27] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::Nothing)
    @ Base ./loading.jl:1554
 [28] top-level scope
    @ stdin:1
ERROR: Failed to precompile Plots [91a5bcdd-55d7-5caf-9e0b-520d859cae80] to [...]/.julia/compiled/v1.8/Plots/jl_OOGuBi.
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] compilecache(pkg::Base.PkgId, path::String, internal_stderr::IO, internal_stdout::IO, keep_loaded_modules::Bool)
   @ Base ./loading.jl:1705
 [3] compilecache
   @ ./loading.jl:1649 [inlined]
 [4] _require(pkg::Base.PkgId)
   @ Base ./loading.jl:1337
 [5] _require_prelocked(uuidkey::Base.PkgId)
   @ Base ./loading.jl:1200
 [6] macro expansion
   @ ./loading.jl:1180 [inlined]
 [7] macro expansion
   @ ./lock.jl:223 [inlined]
 [8] require(into::Module, mod::Symbol)
   @ Base ./loading.jl:1144

I tried moving the GR imports to the @precompile_setup section but it fails as well (import GR; GR.init()).

t-bltg commented 2 years ago

Ping @jheinen for the offending line in GR.jl: https://github.com/jheinen/GR.jl/blob/a79963f1170115d538115587a0ac3af6584086a7/src/funcptrs.jl#L32.

sjkelly commented 2 years ago

I have a WIP PR to fix moves that moves GR to Artifacts and Preferences that should fix this: https://github.com/jheinen/GR.jl/pull/465

I will try to pick this up shortly.

t-bltg commented 2 years ago

Awesome, thanks for working on this !

jheinen commented 2 years ago

So this means, we can no longer use a common distribution with full feature built binaries for different languages/environments? I don't want to oppose this MR, but is it really worth it when other packages have load times that are many times longer?

sjkelly commented 2 years ago

@jheinen You still can. You can use Preferences or overrides in Pkg to distribute your own binaries. If this is regarding my PR in GR; I have not yet completed it yet, as documentation of the new process is the most important part.

From what I can tell, the core problem here is that we can no longer eval in dependencies. So for the case of GR we will always load GR_jll. We then instead use the mechanisms in Pkg (such as Preferences) to change what is dlopened in GR_jll.

t-bltg commented 2 years ago

You can use Preferences or overrides in Pkg to distribute your own binaries

It would be great to add a Preferences usage example in https://github.com/jheinen/GR.jl/pull/465 (maybe in the docs).

jheinen commented 2 years ago

@sjkelly : Thanks for the explanations - I understand. As mentioned by @t-bltg, a Preferences usage example would be great. Hopefully, we can then still use one GR version for different pre-configured REPL environments in our JupyterHub (Julia, Pluto, Python, ...).

jheinen commented 2 years ago

With Preferences, you mean Preferences.jl?

t-bltg commented 2 years ago

Yes, Preferences.jl is meant here.

A real world example (that I often use) is MPI.jl where they have a small package called MPIPreferences.jl which uses Preferences.jl to select different MPI implementations (mpich, openmpi, craympich, ...) and different binaries (artifacts, local builds, ...).

Hopefully, we can then still use one GR version for different pre-configured REPL environments in our JupyterHub

Having an example of usage in a public repository somewhere might help @sjkelly to cover all the specific / targeted GR cases with Preferences.jl.

jheinen commented 2 years ago

@sjkelly : Would the current GR development branch be OK for you?

t-bltg commented 2 years ago

I can confirm that the develop branch solves this issue and would allow https://github.com/JuliaPlots/Plots.jl/pull/4334.

timholy commented 2 years ago

Very cool. Nice work, both! Should this be closed or are there outstanding issues to resolve?

t-bltg commented 2 years ago

I'll close this once https://github.com/JuliaPlots/Plots.jl/pull/4334 is merged ;)

sjkelly commented 2 years ago

@jheinen it looks good to me if you would like to merge. I will circle back around sometime this weekend with some cleanups to the docs and such.