PS. Results for the original input on my box. Going by the the timings
posted above, yours is rather beefier, so this is probably faster than the
current F# version.

(c/quick-bench (nth-shell 2000 (point. 0 0)))
Evaluation count : 6 in 6 samples of 1 calls.
             Execution time mean : 2.956519 sec
    Execution time std-deviation : 22.423970 ms
   Execution time lower quantile : 2.930938 sec ( 2.5%)
   Execution time upper quantile : 2.978283 sec (97.5%)
                   Overhead used : 21.423788 ns


On 22 November 2016 at 05:21, Michał Marczyk <michal.marc...@gmail.com>
wrote:

> Some further optimizations for a factor of ~2.3 speed-up over nth5 as copy
> & pasted from upthread (6.713742 ms → 2.897630 ms) in
>
>   (c/quick-bench (nth-shell 100 (point. 0 0)))
>
> (1) java.util.HashSet has a ctor that takes initial capacity of the set as
> an int. Passing in (* 4 (.size s1)) when constructing s0 – (HashSet. (* 4
> (.size s1)) – gives me a fair speed-up on its own.
>
> (2) Also, (java.util.HashSet. [p]) is a reflective call – replacing that
> with (doto (java.util.HashSet.) (.add p)) seems to result in a tiny, but
> measurable speed-up as well (to 5.245931 ms).
>
> (3) Using an iterator results in a good speed-up, particularly in a
> size-based loop (counting down from (.size current-set) rather than calling
> (.hasNext current-iterator)).
>
> (4) Finally, inlining all the visiting and switching to direct ctor calls
> also helped.
>
> Final code:
>
> (deftype point [^long i ^long j]
>   Object
>   (equals [this that]
>     (and (= (.i this) (.i ^point that))
>          (= (.j this) (.j ^point that))))
>   (hashCode [this]
>     (+ (.i this) (* 4000 (.j this)))))
>
> (defn nth-shell [^long n p]
>   (loop [n  n
>          s1 (doto (HashSet.) (.add p))
>          s2 (HashSet.)]
>     (if (zero? n)
>       s1
>       (let [s0 (HashSet. (* 4 (.size s1)))
>             i1 (.iterator s1)]
>         (dotimes [_ (.size s1)]
>           (let [^point p (.next i1)
>                 i ^long (.i p)
>                 j ^long (.j p)
>                 p1 (point. (dec i) j)
>                 p2 (point. (inc i) j)
>                 p3 (point. i (dec j))
>                 p4 (point. i (inc j))]
>             (if-not (or (.contains s1 p1) (.contains s2 p1))
>               (.add s0 p1))
>             (if-not (or (.contains s1 p2) (.contains s2 p2))
>               (.add s0 p2))
>             (if-not (or (.contains s1 p3) (.contains s2 p3))
>               (.add s0 p3))
>             (if-not (or (.contains s1 p4) (.contains s2 p4))
>               (.add s0 p4))))
>         (recur (dec n) s0 s1)))))
>
> Also, to check that this still outputs the same neighbours the original
> impl (nth*) does:
>
> (defn persistent-shell [shell]
>   (persistent!
>     (reduce (fn [out ^point p]
>               (conj! out [(.-i p) (.-j p)]))
>       (transient #{})
>       shell)))
>
> (= (sort (persistent-shell (nth-shell 500 (point. 0 0))))
>    (sort (nth* 500 [0 0])))
>
> Cheers,
> Michał
>
>
> On 22 November 2016 at 02:03, Didier <didi...@gmail.com> wrote:
>
>> I tried it with the safe equals, and it is slightly slower, but still
>> faster then all others at 4.5ms. The non safe equals gives me 4s. Though
>> this is now within my error margin. If ire-run quick-bench, I sometime get
>> a mean equal for each, so I don't think the instance check adds that much
>> overhead if any at all.
>>
>> @miner: Doesn't using the flag (set! *unchecked-math* :warn-on-boxed)
>> gives me unchecked math automatically? I was under the impression that +,
>> -, /, * etc. would all now perform in an equal way to unchecked-add, etc.
>> If not, what is the difference?
>>
>> @Andy: I tried with the "1.9.0-alpha14" version, and Records were still
>> just as slow as with "1.8.0". Maybe I'm using them wrong.
>>
>> 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/hash
>>> set_benchs
>>>
>>> This is my code for it: https://gist.github.com/didibu
>>> s/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/hash
>>> set_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