using CairoMakie
using Colors
using GeometryBasics: Rect3f
= vec(
positions Point3f(i/5, j/5, k/5) for i=1:7, j=1:7, k=1:7]
[
)
= meshscatter(positions;
fig, ax, obj = Rect3f(Vec3f(-0.5), Vec3f(1.8)),
marker = [RGBA(positions[i]..., 0.5) for i in 1:length(positions)],
color = (; resolution = (1200, 800)),
figure = true,
transparency
)
display(fig)
I’ve been looking for a literate programming environment for Julia, and having just come across J.J. Allaire’s JuliaCon 2022 talk, I’m stunned by how much Quarto is getting right.1
But coming to Quarto from a world of REPL-based development (and having never once voluntarily used a Jupyter notebook…) there was some familiarity with Quarto’s Jupyter-based backend that I needed to develop.
In my case, this need to go digging into how Quarto was working came up in the form of Julia’s notorious time-to-first-plot issue. My documents were taking well over a minute to render, and I’m not even nearly that patient.
Elsewhere in the Juliaverse, this can be improved by the PackageCompiler.jl package, which can compile custom sysimages, including projects’ pre-compiled dependencies, and then Julia can be started with the --sysimage <path>
flag.
But how do you pass arguments to the Julia runtime when you’re starting it via Quarto?
Configuring Quarto’s Julia kernel
Quarto (it turns out) renders using a Jupyter notebook, and Julia support is implemented via a kernel using the IJulia.jl package. Therefore, the trick to customising the Julia runtime in Quarto was to somehow configure that kernel.
Some rummaging around in Jupyter’s config files revealed a kernels
directory, which contained config files for all of the different kernels that had been installed. Each of these had a kernel.json
file, which contained the configuration for that kernel — including an argv
array containing the flags which were passed to the kernel’s respective binary. 🎉
Later searching (once I’d already identified that kernel.json
was a thing) uncovered a command jupyter kernelspec list
which lists all kernels available to Jupyter, which would probably be a smarter way for the next person to go looking for these files:
~ ❱ jupyter kernelspec list
Available kernels:
julia-1.8 /path/to/jupyter/kernels/julia-1.8
etc.
These contain the kernel.json
files, which in Julia’s case looked something like this:
{
"display_name": "Julia 1.8.0",
"argv": [
"/path/to/julia"
"-i",
"--color=yes",
"--project=@.",
"/path/to/IJulia/kernel.jl",
"{connection_file}"
],
"env": {},
"interrupt_mode": "signal",
"language": "julia"
}
So all that needed to be done was for the sysimage path to be passed in as an argument:
"argv": [
# ... args ...
"--sysimage=/path/to/sysimage.dylib",
# ... args ...
],
What about having a dynamic path to the sysimage? Yeah, I don’t know! I’ll maybe update this post if I ever need it, but the --project=@.
argument might give a hint. Otherwise, setting up different kernels for different projects might be the go. Maybe there are Python people screaming at me right now, I don’t know.
Some half-arsed benchmarking
So I’ve successfully customised the Julia kernel config. I think.
Has it actually worked?
Well, here is the plot from the top of this article, rendered directly from this document using CairoMakie (did I not mention this page was being rendered with Quarto? 🙃):
Here are some pretty characteristic results from running this a couple of times with v.s. without a compiled sysimage including Makie (and other packages):
So without the sysimage…
❱ time quarto render
[1/2] 2022/quarto-sysimage/index.qmd
Starting julia-1.8 kernel...Done
Executing 'index.ipynb'
Cell 1/1...Done
[2/2] index.qmd
Output created: _site/index.html
51.97 real 48.89 user 3.48 sys
And with the sysimage…
❱ time quarto render
[1/2] 2022/quarto-sysimage/index.qmd
Starting julia-1.8 kernel...Done
Executing 'index.ipynb'
Cell 1/1...Done
[2/2] index.qmd
Output created: _site/index.html
22.19 real 18.61 user 3.13 sys
Over a 2x speedup. Nice! 👏
Footnotes
This is by comparison to other attempts like VS Code notebooks (no shared language server state across cells) and Pluto.jl, which I genuinely adore as a project except for its exclusive focus on being beginner-friendly at the expense of “advanced” features like — check’s notes — using your own text editor. 🤷↩︎