# Startup time of 1000 packages – 53% slower in Julia 1.12 vs 1.10

**URL:** <https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343>\
**Category:** Internals & Design\
**Tags:** performance, ttfx, latency\
**Created:** [April 23, 2025, 7:36pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343 "2025-04-23T19:36:30Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![fonsp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fonsp/32/222349_2.png) [@fonsp](https://discourse.julialang.org/u/fonsp)\
**Post date:** [April 23, 2025, 7:36pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/1 "2025-04-23T19:36:30Z")

</div>

# Startup time of 1000 packages

I think startup time is a **big deal** for Julia, especially for new users. If it takes a long time to load a package, it makes Julia look bad.

Recently, Julia 1.12 beta was released, and I noticed that the load times are higher than before. Talking to other package developers, this seemed like a shared experience, so I **wrote a script to measure the install, precompile and load times of the 1000 most downloaded packages**.

# Dashboard

To visualize the results, I wrote a Pluto notebook (obviously 😅), you can [read it online here](https://featured.plutojl.org/basic/package%20latency) and play with the parameters yourself.

Here is a screenshot of the notebook, click to go to the interactive version:

[![screenshot of the notebook](https://global.discourse-cdn.com/julialang/original/3X/d/9/d96d60fa8c2e27716d1bef103da6491925e87ad4.png)](https://featured.plutojl.org/basic/package%20latency)

## No complete TTFX measurement

Unfortunately, these numbers do not tell the complete story, since it does not measure JIT compilation. This happens when using packages for the first time, e.g. the first time you call `Plots.plot(data)`. To measure this, we would need a “representative workflow” for every package.

# My take

In my view, package latency has gone up significantly in the last two Julia versions. The average time to install, precompile and load an environment with a popular package is:

- **37% slower in Julia 1.11** compared to Julia 1.10
- **53% slower in Julia 1.12** compared to Julia 1.10

_This is the time it takes to install, (parallel) precompile and `import` a package, plus its dependencies, on a fresh installation of Julia. Average taken over the 1000 most downloaded packages._

My analysis takes installation and precompilation (let’s call this ‘setup’) into account, which not all TTFX discussions do. I believe that setup time is very important for new users, who might face setup times more often (trying new packages), and who might find it most surprising (coming from another ecosystem like Python or Matlab).

## Your take

I am curious to hear what you think of the results! Be sure to play with the parameters in the notebook, edit the code, or run your own benchmarks.

> ## Source code & data
> 
> The script to measure the package loading times is available here:
> 
> [https://github.com/fonsp/package-loading-times](https://github.com/fonsp/package-loading-times)
> 
> You can find the measurements as `.json` files here:
> 
> [https://github.com/fonsp/package-loading-times/tree/results-v1](https://github.com/fonsp/package-loading-times/tree/results-v1)
> 
> You can also get code to load the data from my notebook, see the dashboard for source.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [April 23, 2025, 7:44pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/2 "2025-04-23T19:44:18Z")

</div>

Well, 1.12 is not yet released, so I expect some improvements to come before the final release.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [April 23, 2025, 7:45pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/3 "2025-04-23T19:45:58Z")

</div>

Thank you @fonsp for sharing these concerns. I am also worried with these trends as I am constantly training beginners in Julia, and they spend a _lot of time_ installing packages for the first time.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [April 23, 2025, 7:47pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/4 "2025-04-23T19:47:51Z")

</div>

A big improvement would be if pre-compiled packages could be downloaded and installed without local pre-compilation. I think this will happen at some point in time.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [April 23, 2025, 7:55pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/5 "2025-04-23T19:55:45Z")

</div>

~~Just a meta-comment, I think maybe the title and first paragraph should be edited to make it a bit more clear early on that you’re including the download, installation, and precompile times in your measurements.~~

~~This of course makes sense to measure for Pluto.jl usage where one ends up hitting that pathway very very often whenever making new notebooks, but it’s not what more julia users would typically think of when they hear someone talking about startup times (I at least thought this was about Time-To-Load an already precompiled package until I got to the end of your post).~~

* * *

Personally, I’m less concerned about the precompile times (so long as it’s used to reduce TTFX) than I am about the load times which are totally swamped in this measurement by the precompile times which are orders of magnitude greater. But unfortunately, load times have also been steadily rising since v1.10

* * *

On topic:

I think that the answer to getting down precompile times is going to have to be breaking compilation units into smaller pieces so you don’t have to precompile stuff you don’t actually need (and take greater advantage of parallelism). There’s a good github issue about this here: [Monorepo Packages via Submodules/Subpackages: Support in Pkg and Julia Compiler · Issue #55516 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/55516)

* * *

Edit: somehow I missed that Fons did actually say his script measures those things in the top of the post and I just missed it. Sorry for the noise.

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [April 23, 2025, 8:01pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/6 "2025-04-23T20:01:39Z")

</div>

> [@Mason](#):
>
> you’re including the download, installation, and precompile times in your measurements.
> 
> This of course makes sense to measure for [Pluto.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/Pluto) usage where one ends up hitting that pathway very very often whenever making new notebooks, but it’s not what more julia users would typically think of when they hear someone talking about startup times

Hm, I have to admit I do not share this sentiment. First of all my impression is that download speed is practically instant. I’ve never noticed any meaningful amount of time to download. To install, perhaps? It is not clear to me what “install” even means in the context of Julia. I thought once the source code is downloaded the “installation” _is_ the precompilation…?

In any case, in my typical usecases of Julia I am constantly switching between different projects with different dependencies and different versions. This triggers precompilation surprisingly frequently for reasons I never fully understood (so far I thought that once a package is precompiled it stays as such forever even if you later precompile a newer version, but I guess that’s not the case?). Anyways I am saying this just to provide a data sample of someone that is definitely constantly affected by precompile times. I also notice them to be much longer than TTFX.

* * *

EDIT: Thanks @fonsp for setting this code up!!!

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [April 23, 2025, 8:09pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/7 "2025-04-23T20:09:43Z")

</div>

Just to be clear, I’m not saying precompilation times are not important. They absolutely are.

~~I’m just saying that most people might not read “Startup time” and think “ah of course that includes the download and precompile times”, so the relevant definition should maybe be in the problem statement, or at least accompanying the graph, not down at the bottom.~~

* * *

Edit: somehow I missed that he did actually say his script measures those things in the problem statement. Ignore me, sorry for the noise.

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [April 23, 2025, 8:10pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/8 "2025-04-23T20:10:38Z")

</div>

> [@Datseris](#):
>
> This triggers precompilation surprisingly frequently for reasons I never fully understood (so far I thought that once a package is precompiled it stays as such forever even if you later precompile a newer version, but I guess that’s not the case?).

Same, it seems to be very unpredictable sometimes.  
However, one reason could be that Julia only stores [10 different precompilation files for a package by default](https://docs.julialang.org/en/v1/manual/environment-variables/#env-max-num-precompile-files). In my case, I set `export JULIA_MAX_NUM_PRECOMPILE_FILES=50` to have more files stored.

Also shameless plug: I assembled PrecompileAfterUpdate.jl which avoids the unexpected precompilation after Julia updates. It crawls through your recent manifests and precompiles then whenever you want (for example after a Julia update)

---

<div class="post-metadata">

**Author:** ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)\
**Post date:** [April 23, 2025, 8:33pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/9 "2025-04-23T20:33:29Z")

</div>

> This triggers precompilation surprisingly frequently for reasons I never fully understood

Maybe it is just unlikely that all transitive dependencies are the same across projects that have the same top-level dependencies? If some low-level dependency then has a different version, all downstream packages need to be recompiled (I believe). This plus the limit of 10 versions already mentioned.

---

<div class="post-metadata">

**Author:** ![willow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/willow/32/208918_2.png) [@willow](https://discourse.julialang.org/u/willow)\
**Post date:** [April 23, 2025, 10:08pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/10 "2025-04-23T22:08:09Z")

</div>

Is there any way we can cache and distribute the results of precompilation? I know that some packages invalidate methods in other packages, but is there a way this can work in the common or restricted case?

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [April 23, 2025, 10:38pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/11 "2025-04-23T22:38:33Z")

</div>

I think it should serve as much as precompilation directives of individual packages help now, which is a lot.

---

<div class="post-metadata">

**Author:** ![co1emi11er](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/co1emi11er/32/206849_2.png) [@co1emi11er](https://discourse.julialang.org/u/co1emi11er)\
**Post date:** [April 23, 2025, 11:43pm UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/12 "2025-04-23T23:43:55Z")

</div>

> [@fonsp](#):
>
> Talking to other package developers, this seemed like a shared experience

Yeah. I personally don’t use 1.11 or 1.12 even though I have them installed. 1.10 just feels much better.

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [April 24, 2025, 12:06am UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/13 "2025-04-24T00:06:36Z")

</div>

Me too. And this why I think pushing juliaup that just installs _latest_ is so annoying. I know we can install other versions but first time users do not know that they will be lead into a worst experience that it has to be.

---

<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 24, 2025, 12:41am UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/14 "2025-04-24T00:41:50Z")

</div>

> [@Datseris](#):
>
> I am constantly switching between different projects with different dependencies and different versions. This triggers precompilation surprisingly frequently for reasons I never fully understood (so far I thought that once a package is precompiled it stays as such forever even if you later precompile a newer version, but I guess that’s not the case?).

Different environments can result in different versions of dependencies for a given package and version (AFAIK dependency resolution in practice also isn’t deterministic for a given project, and `Pkg` isn’t an exception), and that forces precompilation of the package itself as a result (say a method inlines a dependency’s method that varies across versions). If you’re just `activate`-ing active environments (environments with `Pkg`-tracked manifests) without `update`-ing or `add`-ing anything though, I would expect what you did within the cache number limit per package. It might also help to make environments inactive by deleting tracked manifests, or better yet untracking them with PkgCleanup.jl.

---

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [April 24, 2025, 12:48am UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/15 "2025-04-24T00:48:33Z")

</div>

I have seen code with many small allocations run 5x faster on julia 1.11 compared to 1.10, so the compiler is doing more work at optimizing some cases.

---

<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 24, 2025, 12:58am UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/16 "2025-04-24T00:58:17Z")

</div>

> [@fonsp](#):
>
> I **wrote a script to measure the install, precompile and load times of the 1000 most downloaded packages**.

Is there also a way to quantify and control for the size of the code base (lines, methods, precompiled methods)? While longer load times could obviously be a good (and routine for AOT libraries) tradeoff for reduced JIT compilation in normal usage (which you pointed out is subjective), it could also just be an increase in features across versions. If those packages are in fact the same versions across Julia versions, then this question would be moot, but they’re probably not.

> [@Mason](#):
>
> I think that the answer to getting down precompile times is going to have to be breaking compilation units into smaller pieces so you don’t have to precompile stuff you don’t actually need (and take greater advantage of parallelism). There’s a good github issue about this here: [Monorepo Packages via Submodules/Subpackages: Support in Pkg and Julia Compiler](https://github.com/JuliaLang/julia/issues/55516)

Seems like the way forward, though I’d actually prefer being able to write the shorter `using OrdinaryDiffEqCore, OrdinaryDiffEqTsit5` instead of `using OrdinaryDiffEq: OrdinaryDiffEqCore, OrdinaryDiffEqTsit5`, with the clearer implication that I only installed and need to load those 2. Not sure, maybe the wider `OrdinaryDiffEq` could be just another bigger dependent that happens to be tied to the dependencies in a monorepo and releases.

---

<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:** [April 24, 2025, 1:21am UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/17 "2025-04-24T01:21:43Z")

</div>

I think it’s worth noting that multiple issues are tracking this across a number of concrete cases, and the compiler team is very well aware of the problem. My own uninformed spidey sense is that there are two major effects here:

- 1.10 was the culmination of a major effort to reduce TTFP and it was hyper optimized to avoid invalidations and the like. 1.11 and 1.12 haven’t seen as much love on that front yet.
- More packages started using PrecompileTools, pushing yet more into package precompilation.

---

<div class="post-metadata">

**Author:** ![tecosaur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tecosaur/32/23206_2.png) [@tecosaur](https://discourse.julialang.org/u/tecosaur)\
**Post date:** [April 24, 2025, 3:26am UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/18 "2025-04-24T03:26:57Z")

</div>

Just as an anecdotal extra, I’ve got a few packages I work on that I am very focused on trying to make as quick to load and run as possible. Across these, I’ve seen and battled with large regressions on 1.11 and 1.12. Looking at `--trace-compile` it seems like this is some part caused by a large increase in invalidations, some of which occur in really hard to understand ways.

For example, BaseDirs.jl is 100% concretely typed with no dependencies. In 1.9 and/or 1.10 (I forget which) loading and using it in another package is near-instant with no invalidations. However, in 1.11/1.12 I see surprising invalidations (only when loaded by another package) that despite extensive trial and error I have been unable to understand or resolve.

I’ve also come across other strange invalidations/(re)compilation appearing in other places. For instance, `using Downloads` takes \<1ms in 1.10, but ~40ms in 1.11. Using `@time_imports using Downloads` shows 130ms to load `NetworkOptions`, 97% compilation time. On a related note, adding up all the times from `@time_imports` will often get a value much larger than `@time using`. I’ve taken to using a difference of hyperfine measurements to actually get load-time results I can trust.

I suspect the large increases that Fons sees here are connected to these sorts of issues I’ve been encountering, as well as the increased precompilation that Matt notes (which surely compounds the harm of invalidation, when code from dependencies is needlessly recompiled in precompilation tasks).

---

<div class="post-metadata">

**Author:** ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)\
**Post date:** [April 24, 2025, 4:04am UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/19 "2025-04-24T04:04:09Z")

</div>

> [@tecosaur](#):
>
> I’ve taken to using a difference of hyperfine measurements to actually get load-time results I can trust.

Would you be willing to expand a bit more on this out of curiosity? What is your method here when you are attempting get load-time results – do you have a mini “load-time” code snippet to show this workflow? Thanks!

---

<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 24, 2025, 4:38am UTC](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343/20 "2025-04-24T04:38:38Z")

</div>

> [@tecosaur](#):
>
> ~40ms in 1.11. Using `@time_imports using Downloads` shows 130ms to load `NetworkOptions`, 97% compilation time.

Oh man I never noticed, this is so weird. NetworkOptions spending so much time precompiling isn’t too surprising given its latest release predates native caching, and package loading varies across sessions especially if some dependencies were already loaded, but it really doesn’t add up (first lines in different REPL sessions):

```julia
julia> @time using Downloads
  0.074386 seconds (66.94 k allocations: 3.905 MiB)

```

```julia
julia> @time using NetworkOptions
  0.009690 seconds (6.05 k allocations: 391.415 KiB)

```

```julia
julia> @time_imports using Downloads # really wish it printed a total
               ┌ 0.0 ms NetworkOptions. __init__ ()
    161.1 ms NetworkOptions 97.77% compilation time
      9.2 ms ArgTools
               ┌ 0.3 ms nghttp2_jll. __init__ ()
      3.6 ms nghttp2_jll
               ┌ 1.7 ms LibCURL_jll. __init__ ()
      4.1 ms LibCURL_jll
               ┌ 0.0 ms MozillaCACerts_jll. __init__ ()
      3.9 ms MozillaCACerts_jll
               ┌ 0.0 ms LibCURL. __init__ ()
      1.9 ms LibCURL
               ┌ 0.4 ms Downloads.Curl. __init__ ()
     21.4 ms Downloads

julia> (161.1+9.2+3.6+4.1+3.9+1.9+21.4)/1000 # total in seconds
0.2052

```

[Next page](https://discourse.julialang.org/t/startup-time-of-1000-packages-53-slower-in-julia-1-12-vs-1-10/128343.md?page=2)
