What is precompiled in a package by default?

I’m sorry if this should be common knowledge, but somehow I haven’t been able to find any explicit information on this. I’m just wondering what (if anything) is precompiled in a package that doesn’t have any precompile statements or similar. So for example, in Example.jl:

Would any method specializations be precompiled? If so, which ones and how are they chosen?

1 Like

I’m interested in this question too!

An easy way to find out would be to use the --trace-compile argument to julia to write all the precompile statements to a file when you load Example.

Here’s how I did that:

The setup:

❯ tree Example
Example
├── Project.toml
└── src
    └── Example.jl

2 directories, 2 files
❯ cat Example/src/Example.jl
module Example
export hello, domath

"""
    hello(who::String)

Return "Hello, `who`".
"""
hello(who::String) = "Hello, $who"

"""
    domath(x::Number)

Return `x + 5`.
"""
domath(x::Number) = x + 5

end

Now run julia with --trace-compile in the Example project, and load Example:

❯ julia --startup=no --trace-compile=precompile.jl --project=Example -e "using Example"

Now lets look at the contents of precompile.jl:

❯ cat precompile.jl
precompile(Tuple{FileWatching.Pidfile.var"##mkpidlock#7", Base.Pairs{Symbol, Integer, Tuple{Symbol, Symbol}, NamedTuple{(:stale_age, :wait), Tuple{Int64, Bool}}}, typeof(FileWatching.Pidfile.mkpidlock), Base.var"#1080#1081"{Base.PkgId}, String, Int32})
precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:reasons,), Tuple{Base.Dict{String, Int64}}}, typeof(Base.compilecache), Base.PkgId, String})
precompile(Tuple{typeof(Base.arg_gen), Tuple{String, String}})
precompile(Tuple{typeof(Base.write), Base.IOStream, UInt32})

Looks like it’s nothing to do with Example, so that tells me nothing is being precompiled here.

I’m a little surprised, since it seems like it’d be a low-hanging fruit to at least precompile the methods with concrete signatures (i.e. hello(::String).

2 Likes

Maybe it’s not done automatically to cut down on load latency? It’s a disadvantage to load code unlikely to be used in that session.

Automatically precompiling concrete signatures could also be a problem for the developer’s control. Say a package has a public function foo, but only a small fraction of its methods need to be precompiled for common usage. If other concrete signatures of foo and all its callees are precompiled automatically, then the developer has to manually opt out, and they can’t simply specify “never precompile foo.” I’d think this is better done with a @precompile_concrete begin ... end around an adjacent set of methods.

Thanks! Ideally I’d like to know the underlying rules, which of course might just be “don’t precompile anything by default”, to really understand what’s going on. But I guess based on what you’ve shown, we can probably assume that this is how it works.