Compile Optimization
Currently Jetzig projects take quite a long time to compile (somewhere around 15-20 seconds). Waiting for a project to recompile after each change is frustrating.
This blog post covers the two strategies taken to reduce compilation speed down to less than 200ms
.
All figures are shown using this website’s codebase to demonstrate compilation of a “real” application. My PC is around ten years old, my CPU is: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
.
Current Implementation
Jetzig currently compiles using LLVM. Zig’s self-hosted compiler is a little incomplete so compiling without LLVM triggers various compiler errors (specifically errors relating to SIMD features).
The very first build of a Jetzig project is very slow:
$ time zig build
real 0m24.161s
user 0m22.910s
sys 0m1.801s
Once the first build is complete, subsequent builds are quicker but still very slow:
$ time zig build
real 0m16.100s
user 0m15.274s
sys 0m1.160s
Waiting for a server to recompile for 16 seconds every time a change is made is not fun.
Optimization 1: Switch to Zig’s self-hosted compiler
Zig’s self-hosted compiler skips the LLVM step entirely. This step is by far the slowest stage of the build process.
The main issue we have is the Zig compiler’s missing implementations for @Vector
SIMD operations.
Luckily these are only used in a few of Jetzig’s dependencies:
Copying from Zig’s stdlib I made a few pull requests (thanks to Karl for merging the PRs on Christmas Day).
After these changes, we were able to use Zig’s self-hosted compiler to build a project:
$ time zig build
real 0m13.688s
user 0m14.593s
sys 0m1.273s
Even the first-time build is faster than an incremental build using LLVM, but the important figure is the incremental re-builds, where most developers spend their time waiting:
$ time zig build
real 0m5.922s
user 0m7.325s
sys 0m0.624s
Around 4-6 seconds is a lot more bearable and is definitely a huge reduction without too much work, but we can do better.
Optimization 2: Use Zig’s incremental compilation
Zig recently added incremental compilation to its nightly build. This allows us to only compile modified code.
After a few changes to Jetzig’s build pipeline and running Zig with --watch -fincremental
we get a build time of 139ms
.
Build Summary: 14/14 steps succeeded
install success
└─ install jetzig.dev success
└─ zig build-exe jetzig.dev Debug native success 139ms
├─ run routes (routes.zig) success 12ms MaxRSS:6M
└─ run routes (routes.zig) (+1 more reused dependencies)
None of the above figures are cherry-picked - we got a real Jetzig application to rebuild from 16 seconds
down to 5.9 seconds
down to 139ms
.
Next Steps
Currently there are two main issues:
- Reloading generated files (e.g. the routes file or Zmpl’s template manifest)
- Dependencies that require LLVM (i.e. they use features not yet implemented in the Zig compiler)
The first issue I’m hopeful that I can find a workaround and then I can merge the changes into main
.
As Zig matures we should see the second issue disappear but, for now, LLVM is likely going to be needed in some capacity for most real-world projects.
Once the changes are merged we can look at browser hot-reloading which could allow developers to make a change to a Jetzig project and see the changes auto-reload in the browser within 200ms.