# Struggling to understand how to use PrecompileTools

**URL:** <https://discourse.julialang.org/t/struggling-to-understand-how-to-use-precompiletools/128419>\
**Category:** Performance\
**Tags:** question\
**Created:** [April 25, 2025, 11:13pm UTC](https://discourse.julialang.org/t/struggling-to-understand-how-to-use-precompiletools/128419 "2025-04-25T23:13:05Z")\
**Posts on this page:** 6\
**Page:** 2

<div class="post-metadata">

**Author:** ![freeman](https://avatars.discourse-cdn.com/v4/letter/f/ec9cab/32.png) [@freeman](https://discourse.julialang.org/u/freeman)\
**Post date:** [April 26, 2025, 10:10pm UTC](https://discourse.julialang.org/t/struggling-to-understand-how-to-use-precompiletools/128419/21 "2025-04-26T22:10:19Z")

</div>

Alright, thank you so much for the thoughtful discussion.

To me this means that making the effort to precompile packages isn’t worth it, as likely I won’t get the benefits. Unless there exists a clear list of “if you do XYZ then you’re guaranteed that your pre-compilation won’t get invalidated”, then from my POV whether or not invalidations will happen depend on Julia internals, and I don’t want to have to learn this specific aspect, which might change with Julia version, or with PrecompileTools version etc. Unless you tell me I’m missing something here…?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [April 26, 2025, 10:16pm UTC](https://discourse.julialang.org/t/struggling-to-understand-how-to-use-precompiletools/128419/22 "2025-04-26T22:16:05Z")

</div>

> [@freeman](#):
>
> I’m running the above experiments in an environment where the package in question is `dev`’d.

Yep, precompilation should occur whether it’s `add` or `dev`, `add` packages just needs extra work after changes so it’s better to `dev` when you’re making many changes quickly.

> [@freeman](#):
>
> The code in B is:
> 
> ```julia
> A.printstats(::Val{:H5}, path) = ImplementationB(path)
> 
> ```

This is definitely type piracy. B did not define any of the input types: `typeof(A.printstats), Val{:H5}, Any`.

To elaborate on danielwe’s explanation of invalidation, you wouldn’t even need separate package caches for a callee to invalidate a compiled method:

```julia
a(b::Bool) = b ? t() : f() # (caller) a(b) calls (the callees) t() or f()
t() = 1

a(true) # compiles here with a UndefVarError for f()

f() = 0 # invalidates dependent a(b::Bool)

a(true) # recompiles here with the defined f()

```

Not sure, but you seemed to imply that the `@precompile_workload printstats(path)` was done in package B that imports A, so B’s cache should have `printstats(path)`, despite not owning the method. If you import B or A then B without nothing in between, I wouldn’t expect B’s cache to be invalidated there.

> [@freeman](#):
>
> Unless there exists a clear list of “if you do XYZ then you’re guaranteed that your pre-compilation won’t get invalidated”, then from my POV whether or not invalidations will happen depend on Julia internals

No, it’s pretty straightforward that if you change a function’s methods, other methods that call that function may have to be recompiled. The hard part is finding where, though type piracy and inherently poor type inference is usually a red flag. I’m also not entirely convinced this is just invalidation.

> [@freeman](#):
>
> The difference that it makes is that `@time @eval ...` now shows 3 seconds, however it still takes 10 seconds to run (wristwatch).

> [@freeman](#):
>
> The first thing I tried was removing the unnecessary imports that existed in that notebook.
> 
> So now `@time @eval ...` prints 3 seconds and it actually takes 3 seconds.

This seems important. What other imports are there and in what order, are they custom packages that do anything to `A.printstats`?

> [@freeman](#):
>
> e.g. if I ran B `@compile_workload` with e.g. 6 packages in its environment (basically, B’s dependencies), then when I tried to use B in any environment that has anything but those same 6 packages, the pre-compilation will be invalidated?

Any environment with B should at least have those 6 dependencies, in the manifest if not the project. Version compatibility with other packages in the environment can result in those dependencies (or B itself) downgrading to previous versions, check `] st` with the environment activated. Environments with changes should automatically precompile their packages (you’ll see a printout) at some point, though you need to restart the session to use it if the packages were already loaded.

> [@danielwe](#):
>
> even if a compiled instance of `printstats(::String)` were invalidated, recompiling that simple method should only take an instant as long as `printstats(::Val{:H5}, ::String)` and everything inside it has already been compiled.

Not necessarily, inlined methods (automatic or `@inline`) only need to be compiled for their caller’s instance. I think whether the method is also separately compiled depends on the circumstance and Julia version; I tried to remake an example showing that an inlined method isn’t separately compiled, but it doesn’t work anymore (v1.11.5).

---

<div class="post-metadata">

**Author:** ![freeman](https://avatars.discourse-cdn.com/v4/letter/f/ec9cab/32.png) [@freeman](https://discourse.julialang.org/u/freeman)\
**Post date:** [April 26, 2025, 10:27pm UTC](https://discourse.julialang.org/t/struggling-to-understand-how-to-use-precompiletools/128419/23 "2025-04-26T22:27:32Z")

</div>

> [@Benny](#):
>
> This is definitely type piracy. B did not define any of the input types: `typeof(A.printstats), Val{:H5}, Any`.

Ok I see your point. At the same time the type piracy in itself is not an issue: I’ll never publish any of these packages, so I have control over how this function and these types are used. The only scenario where something could do wrong is if I tried to a use a public dependency that exports `Val{:H5}`. I’ll take the chance.

I also feel like we’re over-focusing on this specific aspect because that’s what little code I gave you. In reality I just made a few experiments in terms of adding and removing other imports, all of which made different degrees of impact in re-compile time, and none of them have anything to do with my type piracy, as far as I can tell. So I’m confident that Daniel’s guess is likely correct when he said:

> problem may be some of your other dependencies invalidating stuff that’s deeper down in your implementation

The rest of your post reinforces my view that trying to learn and keep up with what Julia can and can’t currently do in terms of avoiding invalidations, and trying to adjust my code to benefit, is not a good investment of time to me.

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [April 26, 2025, 10:29pm UTC](https://discourse.julialang.org/t/struggling-to-understand-how-to-use-precompiletools/128419/24 "2025-04-26T22:29:50Z")

</div>

I don’t have time for a more elaborate reply, but I think the right tool for what you’re trying to do is a dedicated startup package for the notebook environment where you’re using A and B. The purpose of the startup package is just precompilation and nothing else. It should import all the packages you’ll use in the environment, perhaps with `@recompile_invalidations` wrapped around the imports, and then define a precompile workload that includes things like `printstats(filename)`. See the documentation for PrecompileTools.jl for details on how to implement this.

---

<div class="post-metadata">

**Author:** ![freeman](https://avatars.discourse-cdn.com/v4/letter/f/ec9cab/32.png) [@freeman](https://discourse.julialang.org/u/freeman)\
**Post date:** [April 26, 2025, 10:30pm UTC](https://discourse.julialang.org/t/struggling-to-understand-how-to-use-precompiletools/128419/25 "2025-04-26T22:30:52Z")

</div>

Excellent, this would be a solution, thank you.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [April 26, 2025, 10:49pm UTC](https://discourse.julialang.org/t/struggling-to-understand-how-to-use-precompiletools/128419/26 "2025-04-26T22:49:58Z")

</div>

> [@freeman](#):
>
> At the same time the type piracy in itself is not an issue: I’ll never publish any of these packages, so I have control over how this function and these types are used.

Not involving other people does help, but personally practicing type piracy still has all the same risks as multiple people doing it. For example, one of the worst things is two packages accidentally defining the same method signature, causing the surviving method to depend on a load order that can depend on your dependencies’ imports as much as the imports you type e.g. `using B, A` would actually load `A` first because `B` depended on `A`.

> [@freeman](#):
>
> I tried to a use a public dependency that exports `Val{:H5}`

Types aren’t exported, their names are; `export Val{:H5}` won’t even parse, though it will work for a type alias. The types associated with the names `Val`, `Symbol`, or any alias are defined thus owned by `Base`/`Core`, which you are automatically `using`.

> [@freeman](#):
>
> I also feel like we’re over-focusing on this specific aspect because that’s what little code I gave you.

Given your stopwatch results, probably. Invalidations and recompilation could be caused anywhere else, and the timing discrepancy may not even be caused by invalidation. Can’t rule anything out with so little known.

> [@freeman](#):
>
> The rest of your post reinforces my view that trying to learn and keep up with what Julia can and can’t currently do in terms of avoiding invalidations, and trying to adjust my code to benefit, is not a good investment of time to me.

The compile-time benefits (like preventing invalidations) and run-time benefits are worth avoiding type piracy and making the types of variables and expressions in your methods inferrable from the input types, though it’s worth mentioning that limited, controlled poor inference is a feature of a dynamically typed language. That’s just in general, not necessarily related to your issue.

[Previous page](https://discourse.julialang.org/t/struggling-to-understand-how-to-use-precompiletools/128419.md?page=1)
