# Understanding Module Loading: System Image vs. Precompilation Cache

**URL:** <https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431>\
**Category:** Internals & Design\
**Created:** [December 11, 2023, 11:52am UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431 "2023-12-11T11:52:16Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Janis\_Erdmanis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/janis_erdmanis/32/10869_2.png) [@Janis\_Erdmanis](https://discourse.julialang.org/u/Janis_Erdmanis)\
**Post date:** [December 11, 2023, 11:52am UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/1 "2023-12-11T11:52:16Z")

</div>

Hi everyone 👋

I’m curious about the module loading process. Recently, I’ve noticed a shift in recommendations regarding package loading. In the past, there was a focus on incorporating packages like Makie and DifferentialEquations into the system image. Nowadays, it seems there’s more reliance on the precompilation cache.

In my personal experience, I’ve found that creating a custom system image can significantly speed up module loading times. This observation naturally have lead me to wonder: why is using a custom system image faster than simply relying on the precompilation cache? To take this to an extreme, why does Julia need a system image at all if the precompilation cache exists?

While I might be missing something obvious here, I haven’t been able to find a clear explanation for this. (In particular, I asked ChatGPT on this and the answer was rather dogmatic.) Are there any detailed write-ups or documentation that delve into these differences? I’m generally curios in understanding when to prefer one method over the other.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [December 11, 2023, 2:13pm UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/2 "2023-12-11T14:13:53Z")

</div>

The world is indeed shifting beneath your feet: system images have always included native machine code. Before Julia 1.9, precompile caches _did not_. That changed with Julia 1.9:

> **[Julia 1.9 Highlights](https://julialang.org/blog/2023/04/julia-1.9-highlights/#caching_of_native_code)**
>
> Highlights of the Julia 1.9 release.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [December 11, 2023, 2:52pm UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/3 "2023-12-11T14:52:37Z")

</div>

A package’s precompilation cache, a pkgimage, needs to be loaded, or integrated into the existing state. The system image is the initial deserialized state. Both now contain native code.

[https://docs.julialang.org/en/v1/devdocs/sysimg/](https://docs.julialang.org/en/v1/devdocs/sysimg/)

The native code in pkgimages can also be used to build a systrm image as of Julia 1.10 [citation needed].

---

<div class="post-metadata">

**Author:** ![Janis\_Erdmanis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/janis_erdmanis/32/10869_2.png) [@Janis\_Erdmanis](https://discourse.julialang.org/u/Janis_Erdmanis)\
**Post date:** [December 11, 2023, 4:27pm UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/4 "2023-12-11T16:27:28Z")

</div>

Ok, now I see why images are faster, as they are a snapshot of the Julia’s system state. I would like to know a little bit more about what Julia does when it creates a system state from pkgimages. Additionally, what exactly is stored in the system state? How is it organized within Julia?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [December 11, 2023, 5:45pm UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/5 "2023-12-11T17:45:49Z")

</div>

Your mental model about system state is just about perfect — it’s really everything that Julia knows about: types, modules/bindings and their (serializable) values, and of course methods with their specializations (typed, IR, native code, etc.).

As far as how it’s organized, it depends on the category and how deep into internals you want to go. At the Julia-level, types are `subtypes(Any)`, the names of bindings are `names`, values are accessed from the bindings, methods and their specializations are in MethodTables accessed from `methods`.

Incrementally loading a package’s precompile cache needs to track through changes as they happen, e.g., doing subtyping to appropriately insert methods, checking for invalidations, etc.

---

<div class="post-metadata">

**Author:** ![vchuravy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vchuravy/32/8_2.png) [@vchuravy](https://discourse.julialang.org/u/vchuravy)\
**Post date:** [December 11, 2023, 8:26pm UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/6 "2023-12-11T20:26:12Z")

</div>

> [@mbauman](#):
>
> Incrementally loading a package’s precompile cache needs to track through changes as they happen, e.g., doing subtyping to appropriately insert methods, checking for invalidations, etc.

This really is the biggest difference between system images and package images.  
The system image state is always valid, it is the first thing that exists and thus it doesn’t need to perform any work to make sure that things are consistent – by definition they are.

Package images (nee precompilcation caches) have the challenge that they are incremental, they are partial serialization of state and so when we load them we need to perform work to ensure that loading them leads to a consistent state.

We now have decent tooling with Tracy to inspect what is happening during the package loading process.

---

<div class="post-metadata">

**Author:** ![Janis\_Erdmanis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/janis_erdmanis/32/10869_2.png) [@Janis\_Erdmanis](https://discourse.julialang.org/u/Janis_Erdmanis)\
**Post date:** [December 11, 2023, 9:42pm UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/7 "2023-12-11T21:42:33Z")

</div>

> Package images (nee precompilcation caches) have the challenge that they are incremental, they are partial serialization of state and so when we load them we need to perform work to ensure that loading them leads to a consistent state.

Can the construction of a system image state be made simpler if one assumes that the end state derived from given pkgimages is consistent? In such a situation, what operations would still be necessary?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [December 11, 2023, 9:59pm UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/8 "2023-12-11T21:59:06Z")

</div>

I mean, I suppose if you wanted to re-introduce julia#265, you could theoretically try ignoring [invalidations](https://julialang.org/blog/2020/08/invalidations/) and world ages. But that’d make things work very strangely (and probably crashy) once loaded.

Even without that, you’d always need to do the full subtyping to search for where to insert methods since method tables are sorted from most specific to least, and the location of a particular method depends upon what’s already there.

Those are just two aspects of the loading that I’m vaguely familiar with. There’s surely more considerations like the above.

---

<div class="post-metadata">

**Author:** ![StevenSiew](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevensiew/32/218393_2.png) [@StevenSiew](https://discourse.julialang.org/u/StevenSiew)\
**Post date:** [December 11, 2023, 11:40pm UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/9 "2023-12-11T23:40:07Z")

</div>

Can the construction of sysimage be made very very simple like this?

```julia
using Plot,Pkg,Revise

println("Hello World")
savecursysimage("mysysimage001")

```

This would make the startup time very very quick as we know what needs to be in the state of Julia’s memory at startup time by using our customize sysimage at startup time.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [December 12, 2023, 4:35am UTC](https://discourse.julialang.org/t/understanding-module-loading-system-image-vs-precompilation-cache/107431/10 "2023-12-12T04:35:38Z")

</div>

You can do the following.

> **[References · PackageCompiler](https://julialang.github.io/PackageCompiler.jl/stable/refs.html#PackageCompiler.create_sysimage)**
>
> Documentation for PackageCompiler.

```julia-repl
julia> using PackageCompiler

julia> PackageCompiler.create_sysimage(; sysimage_path = "mysysimage001.so");

```
