Improving Compilation Times

As you may have noticed while using Perseus, compiling even very small changes to your app takes a long time. This is because Perseus has a significant amount of boilerplate to make all its features work essentially seamlessly, and because Rust generally takes quite a while to compile anything.

The first step in addressing this is to figure out whether your app needs exporting or not. If not, then you should use #[perseus::main_export] rather than #[perseus::main(...)] in your main.rs, and then you don't need to bring in a server integration, which should speed things up slightly. Notably, if you choose not to do this, there's no actual compile-time difference between perseus export and perseus serve (there used to be, back in the days of .perseus/, but those horrors are gone now). As a general rule, apps that can go to static files should go to static files, because they'll be faster. (It's also more easily possible to push static files to edge networks in deployment, which usually means faster load times for your users, depending on your hosting provider.)

The next step is pretty trivial: rustup override set nightly. This command will set the default toolchain in your project (you have to run that command in your project directory) to nightly, which tends to nearly cut compile times in half! In general, the nightly compiler will be much faster than the stable one, because it uses all sorts of unstable features that improve compilation times. If you want maximum assurances though, switch back to the stable compiler before running perseus deploy so your production app is secured against any nightly compiler bugs (which do happen) and you get the best of both worlds.

From here, we get a little more radical. There's something called Cranelift, a compiler backend that can be used for Rust to speed up development compilation times. Of course, the sacrifice here is runtime speed, but as long as you build with the usual compiler for production, you again get the best of both worlds. To set this up, you'll need to install a precompiled build of rustc_codegen_cranelift.

With that done, any new shell (you may have to log out and back in again) will have cargo-clif supported as a command! You can verify this by confirming that cargo-clif -h results in a help page being printed. This binary acts as a drop-in replacement for cargo, using the Cranelift backend instead. Note that you should only ever use this for development, it's not suitable for production, where you should use the normal cargo instead.

The easiest way to get Perseus to use this, instead of the usual cargo, is to set --cargo-engine-path cargo-clif on perseus (you might do this for a single command, or you might add it to an initialization script to run on all perseus commands). With that done, you can run perseus export -w/perseus serve -w, and, after a while of waiting, you should find your app built! Note that this will take a while the first time because Cranelift has to rebuild everything built with the usual cargo. Note that we only apply this to the engine, since there's no Cranelift support for compiling to Wasm yet. That doesn't matter too much though, since Perseus is internally target-gated enough that Wasm compilations tend to be pretty fast!

Remember: do NOT use cargo-clif in production!

After applying all the optimizations herein, a testing benchmark of measuring the time taken to run perseus build with the bleeding-edge version of the CLI in development mode on the basic example, changing a hardcoded state property, went from taking 28 seconds with the stable compiler and no target directory separation to just 7 seconds, when Cranelift and nightly were used along with the target directory separation now inbuilt into Perseus. In other words, you can cut Perseus' compile times by 75%! (And this was deliberately on a fairly old laptop with other programs running in the background to mimic a realistic setup.)