Search This Blog

Wednesday, January 8, 2014

Starting Clojure from the Command Line

For some reason, starting Clojure is comically slow on my machine. Most people talk about a few seconds, but for me it's more like a minute from 'lein repl' to a usable state.

This is annoying. I need a good reason to restart my repls, and I usually make a cup of tea while it's happening.

Sometimes I come back and find that the thing has failed to start because the start up time has exceeded leiningen's built in time-out and been killed. The reasons this can happen are many and varied, I have found.

This is not the JVM's fault. A trivial java hello world program runs quickly enough that you can't tell that there is a startup time.

One way to speed it up is to get leiningen out of the loop and start clojure as a JVM program, but leiningen is a great classpath manager.

But you can have the best of both worlds:


$ lein classpath > LEIN_CLASSPATH

Gets the classpath and puts it in a file. You only need to do this when you change your project.clj file to add a new dependency.

$ LEIN_CLASSPATH=`cat LEIN_CLASSPATH`




Reads that file into an environment variable.

$ rlwrap java -classpath $LEIN_CLASSPATH clojure.main

Starts clojure with the right classpath. As a bonus, you can use rlwrap to be your command line editor, which works like the bash shell, and is much better than the default.

Usually I use clojure from EMACS though, and so instead I type:

rlwrap java -server -Xmx800M -classpath $LEIN_CLASSPATH clojure.main -e "( do (require 'clojure.tools.nrepl.server) (clojure.tools.nrepl.server/start-server :bind \"127.0.0.1\" :port 4001))" -r

Which starts up an nrepl on port 4001, ready to be bound to from emacs, makes sure the jvm's in server mode, and gives it 800M maximum memory. It will only expand to that size if it needs it, so why not?

In either case clojure will execute a file called user.clj on the classpath, so you've got somewhere to put custom startup code if you like.

If I was feeling really clever, I might turn these commands into a script which remade the file if it was older than project.clj, etc. But these lines are always in my command line history, and that's actually good enough that I don't feel the need (yet).

4 comments:

  1. Very nice. Thanks. Here are a couple of alternatives:

    export LEIN_CLASSPATH=`lein classpath`
    rlwrap java -classpath $LEIN_CLASSPATH clojure.main

    Or in one line:

    rlwrap java -classpath `lein classpath` clojure.main

    which is still a lot faster than 'lein repl'.

    ReplyDelete
  2. I'm using a 2 year old, cheap desktop HP Pavilion (4-core AMD) - nearly the cheapest one they had at Best Buy (around $450 on sale). Currently running Fedora after the Windows HD crashed a year ago. Doing "lein --version" takes only 1 second.

    Timing "lein repl" takes 15 seconds. Timing " java -classpath `lein classpath` clojure.main" takes 5 seconds. It helps, but is it worth the hassle?

    I haven't tried the rlwrap editing yet, but that looks nice.

    Alan

    ReplyDelete
  3. Couple other things you can do to speed this up:

    Set `LEIN_FAST_TRAMPOLINE=y` and use `lein trampoline repl` -- this will make it so that Leiningen itself is never even launched unless it needs to recalculate some state (if the project.clj file changes or something).

    Keep a Leiningen process running in the background and just connect to it via Grenchman: http://leiningen.org/grench.html

    More suggestions on the wiki: https://github.com/technomancy/leiningen/wiki/Faster

    ReplyDelete
  4. Got new computer a year ago from ZaReason. It is middle-of-the-line model, running Ubuntu 15.10 now. On this computer "lein --version" takes only 0.8 - 0.9 second, and "lein repl" only 3.8 seconds. I had tried using drip but it only saved about half a second, so it seems not worth the trouble.

    ReplyDelete

Followers