Search code examples
javascriptperformancenode.jsclojure

Is NodeJs faster than Clojure?


I just started learning Clojure. One of the first things I noticed is that there are no loops. That's OK, I can recur. So let's look at this function (from Practical Clojure):

(defn add-up
  "Adds up numbers from 1 to n"
  ([n] (add-up n 0 0))
  ([n i sum] 
    (if (< n i)
      sum
      (recur n (+ 1 i) (+ i sum)))))

To achieve the same function in Javascript, we use a loop like so:

function addup (n) {
  var sum = 0;
  for(var i = n; i > 0; i--) {
    sum += i;
  }
  return sum;
}

When timed, the results look like:

input size: 10,000,000
clojure: 818 ms
nodejs: 160 ms

input size: 55,000,000
clojure: 4051 ms
nodejs: 754 ms

input size: 100,000,000
clojure: 7390 ms
nodejs: 1351 ms

I then proceeded to try the classic fib (after reading this):

in clojure:

(defn fib
  "Fib"
  [n]
  (if (<= n 1) 1
      (+ (fib (- n 1)) (fib (- n 2)))))

in js:

function fib (n) {
  if (n <= 1) return 1;
  return fib(n-1) + fib(n-2);
}

Again, the performance has quite some difference.

fib of 39
clojure: 9092 ms
nodejs: 3484 ms

fib of 40
clojure: 14728 ms
nodejs: 5615 ms

fib of 41
clojure: 23611 ms
nodejs: 9079 ms

Note I'm using (time (fib 40)) in clojure so it's ignoring the bootup time for JVM. These are ran on a MacBook Air (1.86 Ghz Intel Core 2 Duo).

So what's causing Clojure to be slow here? And why do people say that "Clojure is fast"?

Thanks in advance and Please, no flame wars.


Solution

  • (set! *unchecked-math* true)
    
    (defn add-up ^long [^long n]
      (loop [n n i 0 sum 0]
        (if (< n i)
          sum
          (recur n (inc i) (+ i sum)))))
    
    (defn fib ^long [^long n]
      (if (<= n 1) 1
          (+ (fib (dec n)) (fib (- n 2)))))
    
    (comment
      ;; ~130ms
      (dotimes [_ 10]
        (time
         (add-up 1e8)))
    
      ;; ~1180ms
      (dotimes [_ 10]
        (time
         (fib 41)))
      )
    

    All numbers from 2.66ghz i7 Macbook Pro OS X 10.7 JDK 7 64bit

    As you can see Node.js is trounced. This is with 1.3.0 alphas, but you can achieve the same thing in 1.2.0 if you know what you're doing.

    On my machine Node.js 0.4.8 for addup 1e8 was ~990ms, for fib 41 ~7600ms.

                Node.js  | Clojure
                         |
     add-up       990ms  |   130ms
                         |
     fib(41)     7600ms  |  1180ms