# Tool: sysimage\_creator for IJulia users

**URL:** <https://discourse.julialang.org/t/tool-sysimage-creator-for-ijulia-users/61229>\
**Category:** Tooling\
**Tags:** announcement, plotting, sysimage\
**Created:** [May 16, 2021, 6:03am UTC](https://discourse.julialang.org/t/tool-sysimage-creator-for-ijulia-users/61229 "2021-05-16T06:03:56Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![terasakisatoshi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/terasakisatoshi/32/9187_2.png) [@terasakisatoshi](https://discourse.julialang.org/u/terasakisatoshi)\
**Post date:** [May 16, 2021, 6:03am UTC](https://discourse.julialang.org/t/tool-sysimage-creator-for-ijulia-users/61229/1 "2021-05-16T06:03:57Z")

</div>

# Tool: sysimage\_creator for IJulia users

# Announcement

- Hi, I’m happy to annaunce the release of [sysimage\_creator](https://github.com/terasakisatoshi/sysimage_creator). It will reduce the time to initialize Jupyter Kernel and plot for the first time. Have a try.
- Below I will explain why I made this.

# First motivation

- Have you thought running `using Plots` (first plot) is slow?

Well, you might as well read the following documentation provided by PackageCompiler.jl :

- [https://julialang.github.io/PackageCompiler.jl/dev/examples/plots/#examples-plots-1](https://julialang.github.io/PackageCompiler.jl/dev/examples/plots/#examples-plots-1)

Here is what I did to reporoduce the result written in above. Note that it will generate a [sysimage](https://julialang.github.io/PackageCompiler.jl/dev/sysimages/#sysimages-1) `sys_plots.so`.

```console
% julia -e 'using Pkg; Pkg.add(["Plots", "PackageCompiler"])'
% echo "using Plots; p = plot(rand(5), rand(5)); display(p)" > precompile_plots.jl
% julia -e 'using PackageCompiler; create_sysimage(:Plots, sysimage_path="sys_plots.so", precompile_execution_file="precompile_plots.jl")'
% ls
sys_plots.so # it is generated by PackageCompiler
% julia -q --sysimage sys_plots.so
% julia> @time using Plots
  0.000445 seconds (1.09 k allocations: 81.062 KiB, 648.57% compilation time)
% julia> @time (p = plot(rand(5), rand(5)); display(p))
  0.593317 seconds (222.42 k allocations: 21.338 MiB, 4.90% compilation time)

```

- Yes, it’s actually done well on Julia REPL.

### Utilize our sysimage

- Can we utilize our sysimage `sys_plots.so` for running Julia on Jupyter Notebook/Lab?
- The answer is yes. Before running Jupyter, please install Julia kernel via:

```julia
julia> using IJulia
julia> sysimage = joinpath(@ __DIR__ , "sys_plots.so")
julia> installkernel("Julia-sys-plots", "--project=@.", "--sysimage=$(sysimage)")

```

- It will create a Julia kernel named `Julia-sys-plots` with option `--projecrt=@. --sysimage=/path/to/sys_plots.so`. See [IJulia’s instructions](https://julialang.github.io/IJulia.jl/stable/manual/installation/#Installing-additional-Julia-kernels) to learn more.

- OK. let’s run `jupyter notebook` and then create a Julia notebook its kernel is `Julia-sys-plots 1.6.1` not standard `Julia 1.6.1`. You’ll find running `using Plots; plot(rand(10), rand(10))` is so fast.

- Satisfied? Hmm, have you thought … wait! Go to next.

# Second motivation

- Have you thought Jupyter(with IJulia kernel) starts up so slow? Imagine you want to restart Julia notebook via `Kernel` → `Restart & Run All` several times. You might be a little frustrating because it takes several seconds to make the first cell is updated even if it contains only very simple expression like `1+1`.
- Let’s say we have a Julia notebook named `simple_math.ipynb` which contains only single cell its content is `1+1`. On Julia REPL, the following script will take about 17 seconds. (If you are missing `jupytext` command, please install via `pip install jupytext` or `conda install jupytext -c conda-forge` whatever)

```julia
julia> @time run(`jupytext --execute simple_math.ipynb`)
[jupytext] Reading simple_math.ipynb in format ipynb
[jupytext] Executing notebook with kernel julia-1.6
Starting kernel event loops.
[jupytext] Writing simple_math.ipynb (destination file replaced [use --update to preserve cell outputs and ids])
 17.560112 seconds (44 allocations: 1.875 KiB)
Process(`jupytext --execute simple_math.ipynb`, ProcessExited(0))

```

Note that if you switch the kernel of the notebook to Python, you’ll get:

```python
julia> @time run(`jupytext --execute py_simple_math.ipynb`)
[jupytext] Reading py_simple_math.ipynb in format ipynb
[jupytext] Executing notebook with kernel python3
[jupytext] Writing py_simple_math.ipynb (destination file replaced [use --update to preserve cell outputs and ids])
  2.314238 seconds (44 allocations: 1.875 KiB)
Process(`jupytext --execute py_simple_math.ipynb`, ProcessExited(0))

```

Though it has a little overhead, it is much better than Julia.

- In short, we STILL have a lot of room for improvement to reduce latency. In the next, I will provide a simple prescription to address our issue.

# Prescription

- All right let’s get started. Fisrt, install another Julia kernel to record/trace precompile statements:

```julia
installkernel("Julia-trace-nb", "--project=@.", "--trace-compile=traced_nb.jl")

```

Here we’ve used `--trace-compile` flag to output “precompilation statements” to a file see [instructions for PackageCompiler](https://julialang.github.io/PackageCompiler.jl/dev/devdocs/sysimages_part_1/#Recording-precompile-statements-1) to learn more.

- Second, create notebook named `nb.ipynb` with `Julia-trace-nb 1.6.1` kernel and write expression here as you want e.g. `1+1; using Plots; plot(rand(10), rand(10))` etc…

- Third, execute a command below:

```console
% jupytext --execute nb.ipynb # will generate traced_nb.jl

```

Since, we’ve used Juia kernel with option `--trace-compile=traced_nb.jl`, it will record precompilation statements in `traced_nb.jl`.

- What should we do? Use `create_sysimage` of course. Note that there is a keyword argument `precompile_statements_file` that will accept `"traced_nb.jl"`, above it, we’ve created.

```julia
julia> using PackageCompiler
julia> create_sysimage(
  [:Plots], # you can also add StatsPlots etc...
  sysimage_path="sys_plots_nb.so", 
  precompile_statements_file="traced_nb.jl" # important
)

```

- Finally to test out the sysimage `sys_plots_nb.so`, install new kernel:

```julia
julia> using IJulia
julia> sysimage = joinpath(@ __DIR__ , "sys_plots_nb.so")
julia> installkernel("Julia-sys-plots-nb", "--project=@.", "--sysimage=$(sysimage)")

```

That’s it. Try to create/run Jupyter notebook with `Julia-sys-plots-nb`.

You’ll find `@time run(`jupytext --execute simple\_math-with-sys\_plots\_nb.ipynb`)` tend to be better than `@time run(`jupytext --execute simple\_math.ipynb`)`

# [sysimage\_creator](https://github.com/terasakisatoshi/sysimage_creator)

There are a lot of things to do manually to setup… Do not worry! [sysimage\_creator](https://github.com/terasakisatoshi/sysimage_creator) will save your time to construct environment. Have a try!

```console
git clone https://github.com/terasakisatoshi/sysimage_creator.git
cd sysimage_creator
make # create sysimage
make test # test out !!!

```

---

<div class="post-metadata">

**Author:** ![tfiers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tfiers/32/32427_2.png) [@tfiers](https://discourse.julialang.org/u/tfiers)\
**Post date:** [October 10, 2022, 10:52am UTC](https://discourse.julialang.org/t/tool-sysimage-creator-for-ijulia-users/61229/2 "2022-10-10T10:52:10Z")

</div>

Lovely, thanks for sharing!

When I tried to --trace-compile an IJulia session (manually, not with jupytext) in my own setup earlier, the resulting sysimg could not be used in a kernel (the kernel died immediately). Have you encoutered this issue too when developing this?

Another question, why do you choose `include_transitive_dependencies = false` in `create_sysimage`, is that necessary for it to work?

---

<div class="post-metadata">

**Author:** ![terasakisatoshi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/terasakisatoshi/32/9187_2.png) [@terasakisatoshi](https://discourse.julialang.org/u/terasakisatoshi)\
**Post date:** [October 10, 2022, 1:33pm UTC](https://discourse.julialang.org/t/tool-sysimage-creator-for-ijulia-users/61229/3 "2022-10-10T13:33:02Z")

</div>

Hi @tfiers thank you for finding this article.

> the resulting sysimg could not be used in a kernel (the kernel died immediately).

Did you set `--sysimage` option appropriately?

See [https://github.com/terasakisatoshi/sysimage\_creator/blob/b68e4e608db734c3ceeb0b0f463116a879fad099/installkernel.jl#L22](https://github.com/terasakisatoshi/sysimage_creator/blob/b68e4e608db734c3ceeb0b0f463116a879fad099/installkernel.jl#L22)

As for me, the following commands (described in README) works for me.

```console
$ cd /path/to/sysimage_creator
$ make

```

> Another question, why do you choose `include_transitive_dependencies = false` in `create_sysimage` , is that necessary for it to work?

See [the comment](https://github.com/JuliaLang/PackageCompiler.jl/issues/639#issuecomment-983307378) in this issue [Lazy downloading artifacts with custom sysimage · Issue #639 · JuliaLang/PackageCompiler.jl · GitHub](https://github.com/JuliaLang/PackageCompiler.jl/issues/639)

It seems MKL\_jll downloads when jupyter kernel launches if not set `include_transitive_dependencies=false`

---

<div class="post-metadata">

**Author:** ![tfiers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tfiers/32/32427_2.png) [@tfiers](https://discourse.julialang.org/u/tfiers)\
**Post date:** [October 10, 2022, 9:49pm UTC](https://discourse.julialang.org/t/tool-sysimage-creator-for-ijulia-users/61229/4 "2022-10-10T21:49:17Z")

</div>

I see, thank you.

> [@terasakisatoshi](#):
>
> Did you set `--sysimage` option appropriately?

Yes, I succesfully used an IJulia kernel with a sysimage before; it’s just when I used my sysimage with IJulia-traced precompile statements in it that these crashes happened.

I will try your scripts next time I build a system image, and see if it works for me  
(and if it does, narrow down what the difference is with what I did before).

---

<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:** [June 2, 2023, 8:39pm UTC](https://discourse.julialang.org/t/tool-sysimage-creator-for-ijulia-users/61229/5 "2023-06-02T20:39:33Z")

</div>

Hey @terasakisatoshi, have you played around at all with trying to use these precompile statements with pkgimages instead of sysimages?

I’ve tried a few things now and still have not managed to reduce the startup latency of IJulia without resorting to a sysimage. ☹

---

<div class="post-metadata">

**Author:** ![terasakisatoshi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/terasakisatoshi/32/9187_2.png) [@terasakisatoshi](https://discourse.julialang.org/u/terasakisatoshi)\
**Post date:** [June 3, 2023, 4:48am UTC](https://discourse.julialang.org/t/tool-sysimage-creator-for-ijulia-users/61229/6 "2023-06-03T04:48:09Z")

</div>

Hello @Mason . Sorry for the late reply. Do you reproduce your problem that you have faced in the past with Julia 1.9 (latest as of May 2023)?

---

<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:** [June 3, 2023, 6:23am UTC](https://discourse.julialang.org/t/tool-sysimage-creator-for-ijulia-users/61229/7 "2023-06-03T06:23:20Z")

</div>

yeah, it was the release of v1.9 which prompted me to try this again
