Hello Julien,
I am in a similar situation to yours: I am writing a Clojure library
to parse Starcraft replay files, but my Clojure code is very far from
nearly equivalent Java code. Currently, on my home PC, parsing 1,050
files takes ~70 seconds with Clojure and about 12 with Java. The code
is available at this URL:
http://bitbucket.org/gnuvince/clj-starcraft/.
The Clojure used to take nearly three minutes (even >10 minutes at the
very beginning) to complete. Here are some performance tips that
helped me shave off a lot of processing time.
- (set! *warn-on-reflection* true). This is one of the most important
thing you can do; the Java reflection library is a lot slower than
direct calls (obviously), so make sure that all the type hints are in
place to have direct calls. Put it at the top of your main script and
fix every warning it gives you. It was, for me, the most important
change to make. It brought down performance from over 10 minutes down
to about 3.
- In tight loops, coerce your numbers to their equivalent Java
numerical primitives.
- let bindings are a lot faster to look up than vars. If you have a
var that is looked up very frequently, consider the following idiom to
close over the var:
(def *my-var* <some value>)
(let [local-my-var *my-var*]
(defn my-fn [x y]
...))
- In tight loops, avoid using destructuring binding if you're binding
from a vector. Consider the following snippets:
user> (let [v [1 2 3]] (time (dotimes [_ 1e6] (let [[a b c] v] (+ a b
c)))))
"Elapsed time: 226.992598 msecs"
user> (let [v [1 2 3]] (time (dotimes [_ 1e6] (let [a (v 0), b (v 1),
c (v 2)] (+ a b c)))))
"Elapsed time: 151.938344 msecs"
The second one is definitely uglier, but I was able to go down from 80
seconds to 70 in my program.
- Like David mentioned earlier, use two parameters for the math
primitives.
- -Xprof and -Xrunhprof:cpu=samples are your friends
- A tip given to me on #clojure by Cark: try to "compile" some of your
code down to fns. I was able to get a very appreciable speed boost by
"compiling" my reader functions to fns (see compile-field and compile-
record in unpack.clj)
I hope some of these help. I realize that not all are directly
applicable to your case, but I figure other people may find them
useful.
Cheers,
Vincent.