PrecompileTools doesn't cache result of workload if type unstable

In the following minimal example, foo() isn’t fully precompiled (notice the precompile statement from --trace-compile=stderr) unless I type annotate the type unstable function from Meshes.jl (confirmed type unstable with @code_warntype).
This is a stripped down version of a bigger problem I’m running into. From my understanding though, everything in the precompile workload should get fully compiled (caveat for invalidations, etc., but that shouldn’t be an issue here, I think).

See the precompile output first:

tarnon@dave:~/projects/TestProj$ julia --project=. --trace-compile=stderr -q
julia> using TestProj
[ Info: Precompiling TestProj [36d4f223-0a32-46af-8d45-768600d85ced]
precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:cpu_target,), Tuple{Nothing}}, typeof(Base.julia_cmd)})
precompile(Tuple{Type{NamedTuple{(:stderr, :stdout), T} where T<:Tuple}, Tuple{Base.TTY, Base.TTY}})
precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:stderr, :stdout), Tuple{Base.TTY, Base.TTY}}, typeof(Base.pipeline), Base.Cmd})
precompile(Tuple{typeof(Base._tryrequire_from_serialized), Base.PkgId, String, String})
precompile(Tuple{typeof(Base.first), Array{Any, 1}})

# NOTE: shouldn't foo() be fully precompiled?
julia> foo();
precompile(Tuple{typeof(Base.:(>)), Float64, Int64})

MWE:

module TestProj

using PrecompileTools
using Meshes: area, hull, JarvisMarch, Point2, intersects

export foo

function foo()
    shape1 = hull([Point2(rand(2)) for i in 1:10], JarvisMarch())
    area(shape1) > 1
end

@compile_workload begin
    foo()
end

end

Interestingly, if I type annotate area(shape1)::Float64, the precompile printout goes away.

Is this a bug, invalidation or cache issue, or am I just not using PrecompileTools correctly?

1 Like

While I was surprised myself, this is actually (or apparently) fine. That > specialization gets fully inlined, so there’s no recompilation of foo or anything it needs. There’s also no inference happening, as SnoopCompileCore.@snoopi_deep would show you. I’m still not quite sure I understand why it’s being printed, but the amount of time involved anyway is trivial. Hopefully you’re getting good results from your precompilation.

What would happen if the compiler is not present?

It should be a no-op but I’m unsure it actually is. We’ll certainly find out one way or another when we get serious about working on static compilation…

Thanks for the info (and for your work on PrecompileTools and other tooling packages, which have been a godsend!)

Unfortunately, this proves my MWE isn’t that at all. In my real case, I am getting much more precompilation, involving other libraries like StaticArrays, and which is taking a nontrivial amount of time overall. I thought I had identified the root cause, but I guess I have to look a bit deeper.

I’m marking your answer as the solution, but it’s quite likely I’ll open another issue along similar lines if I can identify another suspicious culprit.

1 Like

Come to think of it, do you know why type annotating the value with ::Float64 would make a difference?

Here’s a likely explanation:

  • >(::Float64, ::Int64) is known to be inlinable
  • Thus we exclude caching a “standalone” version in the sysimage; every inferrable consumer that will need it already has it inlined into the compiled code
  • Without the ::Float64 annotation, it can’t be inlined into foo because type-inference can’t predict which specialization of > will be needed
  • Thus without the ::Float64 annotation, the cached version of foo() does not inline >(::Float64, ::Int64), and hence it needs to be compiled as a stand-alone function (since it’s among the few that we exclude from the cached sysimage).
  • Once you annotate it, inference knows that this is what you’ll be calling, and inlines it. Hence you don’t need to recompile it.
1 Like

Update for posterity, the real situation seems to have been resolved with a more representative precompile workload. The underlying issue seemed to be that one of the Meshes functions relies on dynamic dispatch, and one of those methods wasn’t being precompiled.

1 Like