UPDATE: This post now contains obsolete information. Please read the new ClojureScript Quick Start instead
When I first started programming Clojure circa 2008 startup time
was a legitimate annoyance. Now, on modern hardware starting a bare
Clojure REPL is in subsecond territory - not as snappy as
python, but no longer slow enough for me to care much.
Folks who rely primarily on Leiningen however are less likely to have experienced the pleasant hardware transition of the past few years. No matter how you look at it Leiningen out of the box adds a considerable amount of overhead that cannot be blamed on Clojure itself. For ClojureScript this overhead can become somewhat unbearable due to the level of indirection. The following identifies three fairly straightforward steps to improving ClojureScript cold start compile times.
First, beginning with ClojureScript 0.0-2511 you can now cache compiler
analysis to disk, avoiding unneccessary reading and analysis. Just add
:cache-analysis true entry to your build configuration.
This alone isn't enough to see big gains with respect to cold start compiles. Which brings us to a fairly obscure Leiningen feature, fast trampolines, as well as AOT compilation.
If you set
LEIN_FAST_TRAMPOLINE=y in your shell profile Lein
will cache commands after the first run which side steps the need
to construct an extra JVM. On my poor 2010 laptop avoiding the
extra JVM accounts for a 2X speedup.
I now have the following in my bash
LEIN_FAST_TRAMPOLINE=y export LEIN_FAST_TRAMPOLINE alias cljsbuild="lein trampoline cljsbuild $@"
The next optimization is avoiding compiling ClojureScript itself. You can AOT ClojureScript locally in your project with the following:
lein trampoline run -m clojure.main user=> (compile 'cljs.closure) user=> (compile 'cljs.core)
cljs.core is compiled separately. This is due to the
circular nature of the macros file - it depends on the analyzer .
This will AOT ClojureScript into
target/classes. Just add this
directory to your
:source-paths in your
that if you upgrade ClojureScript you will want to clear
target/classes and recompile.
cljsbuild commands should now be quite a bit snappier.
On my work machine (a 27inch iMac 3.5ghz i7) running
once on a trivial project with no changes used to take around 5.5
seconds. With the above modifications
cljsbuild takes around 1.7
seconds. For kicks I tried the Clojure 1.7.0 fastload branch and
cljsbuild once down to 1 second. That's a 5X performance
boost - I think everyone using Lein would be quite happy for it to
start 5X faster.
Certainly more can and still needs to be done in the ClojureScript compiler itself but these steps eliminate the non-essential overheads.
: This detail was not in the original post. I noticed that nearly
a second was still mysteriously being lost, however I was unable to
isolate the source until I gave YourKit a go.
cljs.analyzer/load-core appeared and the problem became clear, we
were still compiling 1700 lines of Clojure on the fly.