Faster Precompiles with Monorepo?

At work we have a monorepo with all our Julia code in a place like ~/company/src/, and making any change to the codebase triggers precompilation again whenever I start a new notebook or REPL:

julia> using MyModule
Precompiling MyModule
  1 dependency successfully precompiled in 99 seconds. 631 already precompiled.

I would like to solve this, while still using a single git repository. Here are some things I’ve tried:

  • Making a sysimage with PackageCompiler for all the external dependencies (but not our code). This made a big difference on older versions of Julia, but doesn’t seem to matter anymore, perhaps because precompilation is now the default?
  • Using Preferences to try and disable precompilation. This gives me a LocalPreferences.toml file. It triggered a big recompilation of seemingly every dependency, however, using MyModule still has a long precompilation every time
[MyModule]
precompile_workload = false
  • Running commands using SnoopPrecompile and PrecompileTools, which seemed to have the same effect as Preferences
  • Putting __precompile_false__ at the top of my main file. Also didn’t seem to do anything.
  • Using Revise. That helps the symptom, but I often need to spin up new REPLs/notebooks, and Revise can’t help there

Is there anything else I can do? Is there another piece of configuration I need to set up to disable precompilation? Alternatively, is there a way to make it faster while maintaining the monorepo?

You said monorepo, but do you also have a MonoModule? Could you split your git repository into multiple smaller packages and then reexport all the symbols from a MetaModule package? They could all still be in the same repository, but they each would have their own Project.toml.

If you do not want to manage a bunch of Project.toml files, you try the implicit package directory approach.

Another thought is if you had tried julia --pkgimages=no in combination with the system image of all external dependencies.

My final question in all of this is “What is happening during precompilation?”. precompile_workload = false will affect code in a @compile_workload section. Do you have top-level code executing outside a @compile_workload section? Is there a lot happening in your MyModule.__init__()?

1 Like
  1. We currently have a MonoModule, but we could change that. However, all of the guides I found for setting up multiple packages required using git submodules, which we’ve had terrible experiences with. The implicit package directory approach looks like it might avoid that, I’ll try it out.

  2. I tried this and it attempts to precompile my module, then crashes with a segfault:

[3216] signal (11.1): Segmentation fault
in expression starting at REPL[1]:1
Allocations: 7251341 (Pool: 7243658; Big: 7683); GC: 12
Segmentation fault (core dumped)

The sysimage does run without --pkgimages=no, though it still precompiles.

  1. There’s no __init__(), and I don’t currently have a sense of whether there’s a specific part of the codebase that’s generating these long precompiles. We do have a fair number of functions defined through macros, though I’m not sure if that should make a difference.

See this other thread perhaps:

1 Like