Below is the fastest version I tested, using ideas from the various responses 
in this thread. It runs in ~4s on my machine, compared with ~27s for the 
original version.

The biggest win by far was from James Reeves' suggestion of switching to Java's 
mutable HashSet. I'm not sure why; I'd thought that using transients and 
transducers with Clojure's native sets would provide comparable performance, 
but this was not true in my testing.

I also couldn't figure out how to avoid the reflection warning when invoking 
HashSet's constructor that takes a generic Collection, which is why that ugly 
doto is there in nth-neighbors. How would I avoid that?

(ns hash-set-bench
  (import
    [java.util HashSet]
    [java.awt Point]))
  
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
    
(defn neighbors [^Point p]
  (let [x (.-x p), y (.-y p)]
    [(Point. x (inc y))
     (Point. x (dec y)) 
     (Point. (inc x) y)
     (Point. (dec x) y)]))
        
(defn nth-neighbors [^long n ^Point p]
  (loop [n n, s1 (doto (HashSet.) (.add p)), s2 (HashSet.)]
    (if (zero? n) s1
      (let [s0 (HashSet.)]
        (doseq [_ s1, p (neighbors _)]
          (when-not (or (.contains s1 p) (.contains s2 p))
            (.add s0 p)))
        (recur (dec n) s0 s1)))))

> On Nov 15, 2016, at 19:39, Didier <didi...@gmail.com> wrote:
> 
> Hey all,
> 
> I came upon a benchmark of F#, Rust and OCaml, where F# performs much faster 
> then the other two. I decided for fun to try and port it to Clojure to see 
> how Clojure does. Benchmark link: https://github.com/c-cube/hashset_benchs
> 
> This is my code for it: 
> https://gist.github.com/didibus/1fd4c00b69d927745fbce3dcd7ca461a
> 
> (ns hash-set-bench
>   "A Benchmark I modified to Clojure from:
>    https://github.com/c-cube/hashset_benchs";)
> 
> (defn iterNeighbors [f [i j]]
>   (f [(dec i) j])
>   (f [(inc i) j])
>   (f [i (dec j)])
>   (f [i (inc j)]))
> 
> (defn nth* [n p]
>   (loop [n n s1 #{p} s2 #{}]
>     (if (= n 0)
>       s1
>       (let [s0 (atom #{})]
>         (letfn [(add [p]
>                      (when (not (or (contains? s1 p) (contains? s2 p)))
>                        (reset! s0 (conj @s0 p))))]
>                (doseq [p s1] (iterNeighbors add p))
>                (recur (dec n) @s0 s1))))))
> 
> #_(printf "result is %d" (count (time (nth* 2000 [0 0]))))
> 
> And here's the F# code: 
> https://github.com/c-cube/hashset_benchs/blob/master/neighbors2.fsx
> 
> Currently, this takes about 30s in Clojure, while it only takes around 3s for 
> OCaml, Rust and F#.
> 
> From what I see, the differences between my code and theirs are:
>       • Lack of a Point struct, I'm just using a vector.
>       • They use a mutable set, I don't.
>       • They overrode Hashing for their point struct, as well as equality. I 
> rely on Clojure's default hashing, and vector equality.
> I'm not sure if any of these things should really impact performance that 
> much though. And what I could do in Clojure if I wanted to improve it.
> 
> 
> 
> Any Help?
> 
> 
> 
> Thanks.
> 
> 
> -- 
> 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
> --- 
> You received this message because you are subscribed to the Google Groups 
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to