Tricks to make Clojure(startup time) faster?

It bothers me to celebrate this kind of negativity. It also bothers me that this talks about “Clojure being slow” when it’s really “Clojure startup time being slow”.

Clojure is fast, it’s very specifically designed to be fast while maintaining functional semantics. The JVM is super fast once it’s had to time to warm up and JIT code, sometimes faster than the equivalent in lower level languages, despite being a VM.

That starting up Clojure takes time is a long known issue, but let’s get the facts straight. He says it’s a “small 15k loc project”, but later in the Twitter thread he admits that loading dependencies takes 18 out of the 20 seconds. How many LoC is this project including dependencies?

Each Clojure file needs to be compiled into java bytecode, then loaded through a ClassLoader. Both of these are costly operations. This is not unique to Clojure. People also complain about JRuby having slow startup time, but again, JRuby is generally faster than CRuby once it’s warmed up. The JVM is simply not optimized for quickly loading thousands of classes (remember that every Clojure function becomes a class).

This all said, I believe the JRuby team has put a lot of effort in over the years to improve startup time, I’m not sure if that’s as big a priority for Clojure core. Or maybe it is but there’s just less to be gained.

The only thing I’ve seen that seems to be able to work around some of this is this article on using AppCDS and AOT. It seems a pretty advanced approach, but maybe more tooling will start making use of it.

Apart from that the only “trick” is to avoid restarting your process. This is something the Clojure community is quite invested in. You should be able to do everything you need to do from a REPL, and if you need a clean slate you use clojure.tools.namespace to reload your code, and whatever Component/Mount/Integrant-alike you’re using to restart your app state.

Another tool that follows this philosophy and isn’t widely known is grenchman, it gives you a command line tool written in OCaml (starts up fast), which then connects to an nREPL process to do its work. So you can still script things from the shell, but you don’t have to wait 20 seconds for each time you’re calling into Clojure.

Or you can try making a tool like Drip part of your process. I’ve tried that before but I find the hassle of it not worth the small gains. It’ll get you a running JVM faster, but then Clojure still needs to do its work of compiling and loading.

6 Likes