[ANN] PrecompileSignatures.jl: Generate precompile directives by reading method signatures

PrecompileSignatures.jl is a package which reduces TTFX by moving some just in time compilation to the precompilation phase. The package does this by reading all method signatures in a package and generating precompile directives for any concrete signature that it can find.

This idea of this package came when reducing the TTFX for Pluto.jl. At one point we realized that adding one precompile directive reduced the TTFX for one of Pluto’s main methods by 3 seconds (see Use `precompile` for `SessionActions.open` by rikhuijzer · Pull Request #1934 · fonsp/Pluto.jl · GitHub for details). Once we realized that, we started to manually add precompile directives. This worked quite well, but is hard to maintain. With PrecompileSignatures.jl, adding the directives is now automated.

How much this package can reduce the TTFX depends on the package. The more signatures a package has with concretely typed arguments, the more precompile directives can be added. Next, the better the types inside the methods can be infered, the more performance can be gained from adding the directives. This last point depends a lot on Julia’s compiler. Newer Julia versions have a better compiler and better caching which make precompile more effective.

In general, I expect that the packages which are applications such as Pluto.jl or Genie.jl or where methods cannot be easily executed during precompilation due to side-effects will benefit the most from PrecompileSignatures.jl. Note that the precompile directives are generated during the precompilation phase, so there is no need to store separate src/precompile.jl files in your repository.

For more info, see the README.

33 Likes

Just one thing, if I understood well the README the precompilation works for methods whose arguments have been annotated with their type and these are concrete… this isn’t however in contrast to trying to keep the function signatures the more general as possible in order to facilitate code composability ?

3 Likes

As I recall it used to be possible to generate precompile statements from the package’s tests using SnoopCompile.jl
I am pretty sure it still is.
But IIRC it is no longer highlighted as a main use case as there was a reason to avoid adding huge numbers of precompile statements without some thought.
Though I can’t remember what it was. @tim.holy ?

2 Likes

It is easier to just have a small representative workload that runs during precompile time. That will always be updated, even if function names etc change in the package.

5 Likes

Depends on the package. For a package like Pluto there is almost zero need to keep things generic. Especially at the more high-level functions.

5 Likes

Agreed. Or PrecompileSignatures which generates the precompile directives during the precompilation phase. The drawback of a workload is that it can also become outdated and can be problematic when there are side-effects.

PackageCompile, SnoopCompile, workload and PrecompileSignatures all have their drawbacks and benefits IMO.

1 Like

I’m also working on automatically converting some abstract types to concrete. It currently automatically converts AbstractString to String before generating precompile directives. Often, methods precompiled for some subtype of an abstract type are also quicker for other subtypes, so that is why doing this is useful.

I’m still working on making this API public to pass the types and experiment with it. For example, for a package like Distributions, we could precompile all Reals as Float64 since floats are passed into the methods 90% of the time.

3 Likes

Sorry for reviving an old thread but I see the last official release was three years ago.

Is PrecompileSignatures now mature enough that no additional work is being planned, has it been superceded by other efforts, or is it simply dormant?

I think GitHub - JuliaLang/PrecompileTools.jl: Reduce time-to-first-execution of Julia code is the way to go nowadays

Thanks for the response.

It’s a shame because I liked the intermediate approach it provided, where I could simply:

@precompile_signatures(module_name)

and be on my way…

You can still use this. As the original post and links suggest, an actively updated Pluto.jl still uses PrecompileSignatures.jl (just check the Project.toml files), so there’s a strong incentive for the latter to keep working despite the lack of recent developments.

It was never superseded, it was always a rarer approach for less generic packages with concretely typed methods that wouldn’t explode in number. Precompilation has changed a bit in the last 3 years, but that aspect hasn’t.

2 Likes

Thanks @Benny

How about Speculator.jl: Reduce latency through speculative compilation ?

1 Like

For anyone now confused about what method to use: try everything that seems useful and run benchmarks. Only benchmarks will tell you what works and what doesn’t work.

1 Like

Yeah, I saw the announcement, which led me to go back and check what I was trying previously, which led me to asking the question above.

It’s awesome work and I’m grateful for all the effort the community puts in, but opens the question of what to use, what the differences are, and when all of this is will become handled auto-magically and I as a user can stop worrying about it :winking_face_with_tongue: