Skip to content

Commit 0c0fdf0

Browse files
start docs section about loading/precomp/ttfx time
1 parent 8cae8d1 commit 0c0fdf0

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

doc/src/manual/performance-tips.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,116 @@ Prominent examples include [MKL.jl](https://github.com/JuliaLinearAlgebra/MKL.jl
13941394
These are external packages, so we will not discuss them in detail here.
13951395
Please refer to their respective documentations (especially because they have different behaviors than OpenBLAS with respect to multithreading).
13961396

1397+
## Execution latency, loading and precompiling time
1398+
1399+
### Reducing time to first plot etc.
1400+
1401+
The first time a julia function is called it will be compiled. The [`@time`](@ref) macro family illustrates this.
1402+
1403+
```
1404+
julia> foo() = rand(2,2) * rand(2,2)
1405+
foo (generic function with 1 method)
1406+
1407+
julia> @time @eval foo();
1408+
0.252395 seconds (1.12 M allocations: 56.178 MiB, 2.93% gc time, 98.12% compilation time)
1409+
1410+
julia> @time @eval foo();
1411+
0.000156 seconds (63 allocations: 2.453 KiB)
1412+
```
1413+
1414+
Note that `@time @eval ...` form is explained in the [`@time`](@ref) documentation.
1415+
1416+
When developing a package, it can be helpful to cache this compilation in the package caches that are "precompiled"
1417+
during the `Pkg` installation process or before package load. To precompile package code effectively, it's recommended
1418+
to use [`PrecompileTools.jl`](https://julialang.github.io/PrecompileTools.jl/stable/) to run a "precompile workload" during
1419+
precompilation time that is representative of typical package usage, which will cache the native compiled code into the
1420+
package `pkgimage` cache, greatly reducing "time to first ..." (often referred to as TTFX) for such usage.
1421+
1422+
1423+
### Reducing package loading time
1424+
1425+
When keeping the time taken to load the package down is usually helpful.
1426+
General good practice for package developers includes:
1427+
1428+
1. Balancing the need to depending on packages for functionality vs. their load times, including stdlibs which on newer
1429+
julia versions may not be loaded already as stdlibs are no longer always included in the default sysimage.
1430+
2. Avoiding adding `__init__` functions to package modules unless unavoidable, especially those which might trigger a
1431+
lot of compilation, or just take a long time to execute.
1432+
3. Watch out for invalidations in the dependency tree during load that could be causing significant recompilation downstream.
1433+
1434+
The `InteractiveUtils` tool [`@time_imports`](@ref) can be useful in the REPL to review the above factors.
1435+
1436+
```julia-repl
1437+
julia> @time @time_imports using Plots
1438+
0.5 ms Printf
1439+
16.4 ms Dates
1440+
0.7 ms Statistics
1441+
┌ 23.8 ms SuiteSparse_jll.__init__() 86.11% compilation time (100% recompilation)
1442+
90.1 ms SuiteSparse_jll 91.57% compilation time (82% recompilation)
1443+
0.9 ms Serialization
1444+
┌ 39.8 ms SparseArrays.CHOLMOD.__init__() 99.47% compilation time (100% recompilation)
1445+
166.9 ms SparseArrays 23.74% compilation time (100% recompilation)
1446+
0.4 ms SparseArraysExt
1447+
0.5 ms TOML
1448+
8.0 ms Preferences
1449+
0.3 ms PrecompileTools
1450+
0.2 ms Reexport
1451+
... many deps omitted for example ...
1452+
1.4 ms Tar
1453+
┌ 73.8 ms p7zip_jll.__init__() 99.93% compilation time (100% recompilation)
1454+
79.4 ms p7zip_jll 92.91% compilation time (100% recompilation)
1455+
┌ 27.7 ms GR.GRPreferences.__init__() 99.77% compilation time (100% recompilation)
1456+
43.0 ms GR 64.26% compilation time (100% recompilation)
1457+
┌ 2.1 ms Plots.__init__() 91.80% compilation time (100% recompilation)
1458+
300.9 ms Plots 0.65% compilation time (100% recompilation)
1459+
1.795602 seconds (3.33 M allocations: 190.153 MiB, 7.91% gc time, 39.45% compilation time: 97% of which was recompilation)
1460+
1461+
```
1462+
1463+
Notice that in this example there are multiple packages loaded, some with `__init__()` functions, some of which cause
1464+
compilation of which some is recompilation.
1465+
1466+
This report gives a good opportunity to review whether the cost of dependency load time is worth the functionality it brings.
1467+
Also the `Pkg` utility `why` can be used to report why a an indirect dependency exists.
1468+
1469+
```
1470+
(CustomPackage) pkg> why FFMPEG_jll
1471+
Plots → FFMPEG → FFMPEG_jll
1472+
Plots → GR → GR_jll → FFMPEG_jll
1473+
```
1474+
1475+
or to see the indirect dependencies that a package brings in, you can `pkg> rm` the package, see the deps that are removed
1476+
from the manifest, then revert the change with `pkg> undo`.
1477+
1478+
If loading time is dominated by slow `__init__()` methods having compilation, one verbose way to identify what is being
1479+
compiled is to use the julia args `--trace-compile=stderr --trace-compile-timing` which will report a [`precompile`](@ref)
1480+
statement each time a method is compiled, along with how long compilation took. For instance, the full setup would be:
1481+
1482+
```
1483+
$ julia --startup-file=no --trace-compile=stderr --trace-compile-timing
1484+
julia> @time @time_imports using CustomPackage
1485+
...
1486+
```
1487+
1488+
Note the `--startup-file=no` which helps isolate the test from packages you may have in your `startup.jl`.
1489+
1490+
More analysis of the reasons for recompilation can be achieved with the
1491+
[`SnoopCompile`](https://github.com/timholy/SnoopCompile.jl) package.
1492+
1493+
### Reducing precompilation time
1494+
1495+
If package precompilation is taking a long time, one option is to set the following internal and then precompile.
1496+
```
1497+
julia> Base.PRECOMPILE_TRACE_COMPILE[] = "stderr"
1498+
1499+
pkg> precompile
1500+
```
1501+
1502+
This has the effect of setting `--trace-compile=stderr --trace-compile-timing` in the precompilation processes themselves,
1503+
so will show which methods are precompiled and how long they took to precompile.
1504+
1505+
There are also profiling options such as [using the external profiler Tracy to profile the precompilation process](@ref Profiling-package-precompilation-with-Tracy).
1506+
13971507

13981508
## Miscellaneous
13991509

0 commit comments

Comments
 (0)