Here's my findings: Speed increase from most increase to least: * Pre-sizing the HashSet - from 4.7ms to 3.7ms * Inlining - from 4.7ms to 3.9ms * Using point. constructor instead of ->point - from 2.4ms to 2ms * Using non relfective HashSet init - from 2.36ms to 2.17ms * Using iterator instead of doseq - from 2.17ms to 2.07ms
All in all, after this whole experiment, those would be my go to recommendations if wanting to optimize something in Clojure: - If you are creating a lot of temporary structures use deftype for optimum performance, avoid records, and go for vectors and maps as a good middle ground. - If you are adding a lot to a data-structure, resort to a mutable version for best performance. - If using a mutable data-structure, try to pre-size them, if you plan on them growing a lot. - If performance is critical, try inlining the functions you call most, things called from a loop are great candidates. - Avoid using ->wtv style constructors, prefer wtv. instead for best performance. - Avoid reflection and checked boxed math, especially if inside a loop. - Try an iterator as the fastest way to iterate over a structure, though at this point, you're probably spending too much time optimizing. On Tuesday, 15 November 2016 19:39:43 UTC-8, Didier 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 [email protected] Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to [email protected] 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 [email protected]. For more options, visit https://groups.google.com/d/optout.
