When I read about the clojure performance tips, I was quite amused. Well, the first two tips are OK. Clojure tries to be dynamic, and I cannot think of a dynamic language living in the JVM not using reflection in some cases. So type hints can be a good thing to optimize. Not using Wrapper-Classes but using primitive types also seems like something plausible – tell the Compiler that your number stays small enough so it can optimize.
But then… Use binary arithmetic operators. WTF? It is slower to say (+ 2 3 4) than to say (+ 2 (+ 3 4))? Because „For a while now, Clojure has supported inlining of certain expressions. For arithmetic operations, only the calls with exactly two arguments will be inlined.“ In his example, I thought, maybe passing the thing directly to the REPL makes it slower because it doesnt compile the expression. So I defined functions doing the same:
Clojure 1.0.0- user=> (def hallo (fn  (dotimes [_ 1e7] (+ 2 4 5)))) #'user/hallo user=> (def hallo2 (fn  (dotimes [_ 1e7] (+ 2 (+ 4 5))))) #'user/hallo2 user=> (time (hallo)) "Elapsed time: 5008.579881 msecs" nil user=> (time (hallo2)) "Elapsed time: 1650.281717 msecs" nil user=>
So actually, it seems true – sorry, to me this just shows that that compiler isnt really sufficiently intelligent for being a lisp-compiler – remember: lisp programmers know the value of everything and the cost of nothing (Alan Perlis). The other thing is that these functions are constant – they do not depend on any arguments or side-effects. Even a C-Compiler would just have replaced our definitions with something that just always returns the same value, nil in our case. And at least an arithmetic expression should be replaced if its constant (if not the whole loop).
Then … why the fuck is destructing binding slower than accessing a vector directly? Running the example from the above link gives other times (my computer may be slower), but the same outcome:
user=> (let [v [1 2 3]] (time (dotimes [_ 1e7] (let [[a b c] v] a b c)))) "Elapsed time: 2438.966297 msecs" nil user=> user=> (let [v [1 2 3]] (time (dotimes [_ 1e7] (let [a (v 0) b (v 1) c (v 2)] a b c)))) "Elapsed time: 1649.46514 msecs" nil
Actually, I do not understand why both codes are not doing exactly the same? The first one should – imho – only be short for the second one, shouldnt it? And wtf? Making code less readable for performance issues? I thought this wants to be a lisp! In lisp you fit the language to your problem, not your problem to the language!
Anyway, its version 1.0 now – I hope they will optimize it soon. Before it becomes 2.0. I dont like clojure (for other reasons), its a step into a direction I dont really like, but at least it is a new step.
I am surprized that this post became that popular, and actually, this makes me a little sad, because I have already written more interesting stuff (at least as far as I think) noone cared about.
It is criticism about a few misfeatures in the language, which I think are major issues and should be corrected as soon as possible, but dont make clojure worse. I dont like clojure much, I have reasons for it (already posted a few of them – and no, the JVM is not a reason), but as I said, it is a step into a direction I dont like, but at least it is a step – and Lisp needs new steps.
And of course, this post is meant to be provocative (in an ironical way – I thought that was clear), so the fact that it raised discussions shows that it served its purpose.