I have found Clojure to be consistently faster than Python, so I
thought it would be instructive to compare the Python code to the
closest Clojure equivalent.

Here's the originally posted Python code:
from itertools import repeat

def convolve(ns, ms):
    y = [i for i in repeat(0, len(ns)+len(ms)-1)]
    for n in range(len(ns)):
        for m in range(len(ms)):
            y[n+m] = y[n+m] + ns[n]*ms[m]
    return y

Now, if you want to write this functionally in Clojure, you've got two
main obstacles in your way.  First, you can't "bang a vector in place"
-- you want to use Clojure's built-in persistent vectors.  For the
best speed, you can call transient at the beginning of the operations
and persistent! at the end, but you still need to pass the vector
around as an accumulator.  This leads to the second obstacle --
because you're not "banging a vector in place" you can't really use
for loops or doseq loops to conveniently iterate through the indices.
You need to unroll these for loops into loop/recur constructs.  It's a
bit of a nuisance to do all this, but it's actually a fairly
straightforward translation process.  Here's what you get:

(defn convolve [ns ms]
  (let [ns (vec ns), ms (vec ms),
        count-ns (count ns), count-ms (count ms)]
    (loop [n 0,
           y (transient (vec (repeat (dec (+ count-ns count-ms)) 0)))]
      (if (= n count-ns) (persistent! y)
          (recur (inc n)
                 (loop [m 0, y y]
                   (if (= m count-ms) y
                       (let [n+m (+ n m)]
                         (recur (inc m) (assoc! y n+m
                                                (+ (y n+m) (* (ns n) (ms 
m)))))))))))))

By my measurements, this Clojure code runs (under java -server) twice
as fast as the comparable Python code.  Considering that Clojure's
math is inherently slower than Python's, and Clojure is using more
expensive persistent data structures, it's impressive that Clojure is
faster.  Clojure also offers the opportunity to go faster (with Java
arrays), and even to deliver the results lazily if that's useful to
you (but unsurprisingly, the laziness comes at a bit of a cost).
Others have described those solutions, so I won't rehash those here.

But there are two strikes against Clojure here:
1.  This Clojure code is definitely harder to read than the Python
equivalent.  The transformation obfuscates what's going on.
2.  Running the Python code under the psyco JIT compiler speeds the
Python code up by a factor of 10, making it about 5 times faster than
this Clojure transformation.  So Python still gets the final word in
speed here (comparing the exact same algorithm on their built-in data
structures).

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to