Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-09 Thread Frantisek Sodomka

Jonathan showed destructuring/indexing very nicely. Here are some
timings for vector/list creation:

(let [lst '(1 2 3)]
  (timings 1e7
(list 1 2 3)
(cons 1 (cons 2 (cons 3 (
(conj (conj (conj () 3) 2) 1)

(vector 1 2 3)
(vec lst)
(conj (conj (conj [] 1) 2) 3)))

 6590.33 ms   61.2%   1.6x  (list 1 2 3)
 7571.30 ms   70.3%   1.4x  (cons 1 (cons 2 (cons 3 (
 7773.67 ms   72.2%   1.4x  (conj (conj (conj () 3) 2) 1)
 9601.74 ms   89.1%   1.1x  (vector 1 2 3)
 8612.29 ms   79.9%   1.3x  (vec lst)
10772.88 ms  100.0%   1.0x  (conj (conj (conj [] 1) 2) 3)

Yes, it looks that list creation is faster than vector creation.

Frantisek

On Jul 9, 12:17 am, John Harrop  wrote:
> On Wed, Jul 8, 2009 at 4:57 PM, Frantisek Sodomka wrote:
>
> > So far it seems that vectors win in Clojure:
>
> > (timings 3e5
> >  (let [v (vector 1 2 3) a (nth v 0) b (nth v 1) c (nth v 2)] (+ a b
> > c))
> >  (let [lst (list 1 2 3) a (nth lst 0) b (nth lst 1) c (nth lst 2)] (+
> > a b c)))
>
> > =>
> >  680.63 ms   83.6%   1.2x  (let [v (vector 1 2 3) a (nth v 0) b (nth
> > v 1) c (nth v 2)] (+ a b c))
> >  813.79 ms  100.0%   1.0x  (let [lst (list 1 2 3) a (nth lst 0) b
> > (nth lst 1) c (nth lst 2)] (+ a b c))
>
> Does using vec instead of vector make a difference? Using first, rest,
> first, rest instead of nth to destructure the list?
--~--~-~--~~~---~--~~
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
-~--~~~~--~~--~--~---



Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-08 Thread John Harrop
On Wed, Jul 8, 2009 at 4:57 PM, Frantisek Sodomka wrote:

>
> So far it seems that vectors win in Clojure:
>
> (timings 3e5
>  (let [v (vector 1 2 3) a (nth v 0) b (nth v 1) c (nth v 2)] (+ a b
> c))
>  (let [lst (list 1 2 3) a (nth lst 0) b (nth lst 1) c (nth lst 2)] (+
> a b c)))
>
> =>
>  680.63 ms   83.6%   1.2x  (let [v (vector 1 2 3) a (nth v 0) b (nth
> v 1) c (nth v 2)] (+ a b c))
>  813.79 ms  100.0%   1.0x  (let [lst (list 1 2 3) a (nth lst 0) b
> (nth lst 1) c (nth lst 2)] (+ a b c))
>

Does using vec instead of vector make a difference? Using first, rest,
first, rest instead of nth to destructure the list?

--~--~-~--~~~---~--~~
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
-~--~~~~--~~--~--~---



Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-08 Thread Jonathan Smith

Seperate so it can be easily ignored:

Changed in core.clj:

(defn nth
  "Returns the value at the index. get returns nil if index out of
  bounds, nth throws an exception unless not-found is supplied.  nth
  also works for strings, Java arrays, regex Matchers and Lists, and,
  in O(n) time, for sequences."
  {:inline (fn ([c i] `(. clojure.lang.RT (nth ~c ~i)))
   ([c i n] `(. clojure.lang.RT (nth ~c ~i ~n
   :inline-arities #{2 3}}
  ([coll index] (. clojure.lang.RT (nth coll index)))
  ([coll index not-found] (. clojure.lang.RT (nth coll index not-
found
;;changed to inline the 3ary version of nth


(defn destructure [bindings]
  (let [bmap (apply array-map bindings)
pb (fn pb [bvec b v]
   (let [pvec
 (fn [bvec b val]
   (let [gvec (if (symbol? val) val (gensym
"vec__")),
 length-val (:length ^val)]

 (loop [ret (if (symbol? val) bvec (-> bvec
(conj gvec) (conj val)))
n 0
bs b
seen-rest? false]
   (if (seq bs)
 (let [firstb (first bs)]
   (cond
(= firstb '&) (recur (pb ret (second
bs) (list `nthnext gvec n))
 n
 (nnext bs)
 true)
(= firstb :as) (pb ret (second bs)
gvec)
:else (if seen-rest?
(throw (new Exception
"Unsupported binding form, only :as can follow & parameter"))
(recur (pb ret firstb (if (and
length-val (< n length-val))
(if (or 
(vector? val)(= (:tag ^val) :vector))
(list 
gvec n)
(list 
`nth gvec n))
(list `nth gvec 
n nil)))
   (inc n)
   (next bs)
   seen-rest?
 ret
 pmap
 (fn [bvec b v]
   (let [gmap (or (:as b) (if (symbol? v) v
(gensym "map__")))
 defaults (:or b)]
 (loop [ret (if (symbol? v) bvec (-> bvec
(conj gmap) (conj v)))
bes (reduce
 (fn [bes entry]
   (reduce #(assoc %1 %2 ((val
entry) %2))
   (dissoc bes (key
entry))
   ((key entry) bes)))
 (dissoc b :as :or)
 {:keys #(keyword (str %)), :strs
str, :syms #(list `quote %)})]
   (if (seq bes)
 (let [bb (key (first bes))
   bk (val (first bes))
   has-default (contains? defaults
bb)]
   (recur (pb ret bb (if has-default
   (list `get gmap bk
(defaults bb))
   (list `get gmap
bk)))
  (next bes)))
 ret]
 (cond
  (symbol? b) (-> bvec (conj b) (conj v))
  (vector? b) (pvec bvec b v)
  (map? b) (pmap bvec b v)
  :else (throw (new Exception (str "Unsupported
binding form: " b))
process-entry (fn [bvec b] (pb bvec (key b) (val b)))]
(if (every? symbol? (keys bmap))
  bindings
  (reduce process-entry [] bmap

;;I wasn't sure how to do a 'vector' (kept getting the 'vector'
function instead) so I just used
;;:vector keyword to dispatch  on. Becomes kind of unsafe if you
violate the 'length' promise.
;;this also eliminates a redundancy where you were binding (let [v [1
2 3]] (let [[a b c] v]))
;;and the inner let would have (let [gensym_vec v, a (nth gensym_vec
0)...])
;;as opposed to (let [a (nth v 0)...]...)
;;which should be ok because we don't have to worry about multiple
evaluation if we are destructuring from a symbol.
--~--~-~--~~~---~--~~
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

Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-08 Thread Jonathan Smith

On Jul 8, 4:57 pm, Frantisek Sodomka  wrote:
> So far it seems that vectors win in Clojure:
>
> (timings 3e5
>   (let [v (vector 1 2 3) a (nth v 0) b (nth v 1) c (nth v 2)] (+ a b
> c))
>   (let [lst (list 1 2 3) a (nth lst 0) b (nth lst 1) c (nth lst 2)] (+
> a b c)))
>
> =>
>   680.63 ms   83.6%   1.2x  (let [v (vector 1 2 3) a (nth v 0) b (nth
> v 1) c (nth v 2)] (+ a b c))
>   813.79 ms  100.0%   1.0x  (let [lst (list 1 2 3) a (nth lst 0) b
> (nth lst 1) c (nth lst 2)] (+ a b c))
>
> Frantisek
>
> On Jul 8, 7:29 pm, John Harrop  wrote:
>
> > Interesting. How are these timings affected if you add in the time taken to
> > pack the list or vector in the first place, though? I have the feeling it
> > may be slightly cheaper to unpack a vector, but noticeably cheaper to pack a
> > list...
>
>


I did some playing and it seems that the x-factor is really whether or
not the array access function is in-lined into the code. I made a few
tweaks to the source code so that the destructuring bind is able to
rewrite the code with the 2-ary version and avoid rebinding overhead.
I'll post it in reply to this.

Here are some results I collected:
(I like your 'timings' macro, by the way).

(let [v [1 2 3] lst '(1 2 3)]
  (timings 1e7
(let [[a b c]  v] a b c)
(let [[a b c] #^{:length 3} v] a b c)
(let [[a b c] #^{:tag :vector :length 3} v] a b c)
(let [[a b c]  #^{:length 3} [1 2 3]] a b c)
(let [a (v 0) b (v 1) c (v 2)] a b c)
(let [a (nth v 0) b (nth v 1) c (nth v 2)] a b c)
(let [a (first v) b (second v) c (first (rest (rest v)))] a b c)
(let [x (first v) r1 (rest v) y (first r1) r2 (rest r1) z (first
r2)] x y z)

(let [[a b c] lst] a b c)
;; (let [a (lst 0) b (lst 1) c (lst 2)] [a b c])
(let [a (nth lst 0) b (nth lst 1) c (nth lst 2)] a b c)
(let [a (first lst) b (second lst) c (first (rest (rest lst)))] a
b c)
(let [x (first lst) r1 (rest lst) y (first r1) r2 (rest r1) z
(first r2)] x y z)))


---
no inlining of 3-ary version (inlines 2ary version as normal),
rewriting to 2-ary version dependant on :length :tag metadata and
 4650.19 ms   34.2%   2.9x  (let [[a b c] v] a b c) ;;nothing
 3675.41 ms   27.0%   3.7x  (let [[a b c] v] a b c) ;;length
 3414.78 ms   25.1%   4.0x  (let [[a b c] v] a b c) ;; tag length
13606.68 ms  100.0%   1.0x  (let [[a b c] [1 2 3]] a b c)
 3459.74 ms   25.4%   3.9x  (let [a (v 0) b (v 1) c (v 2)] a b c)
 3551.73 ms   26.1%   3.8x  (let [a (nth v 0) b (nth v 1) c (nth v 2)]
a b c)
 9810.52 ms   72.1%   1.4x  (let [a (first v) b (second v) c (first
(rest (rest v)))] a b c)
 9234.34 ms   67.9%   1.5x  (let [x (first v) r1 (rest v) y (first r1)
r2 (rest r1) z (first r2)] x y z)
 9519.99 ms   70.0%   1.4x  (let [[a b c] lst] a b c)
 7131.29 ms   52.4%   1.9x  (let [a (nth lst 0) b (nth lst 1) c (nth
lst 2)] a b c)
 7665.99 ms   56.3%   1.8x  (let [a (first lst) b (second lst) c
(first (rest (rest lst)))] a b c)
 7490.44 ms   55.0%   1.8x  (let [x (first lst) r1 (rest lst) y (first
r1) r2 (rest r1) z (first r2)] x y z)

---
inlining 3ary version, rewriting dependant on :length and :tag.
 3230.53 ms   24.6%   4.1x  (let [[a b c] v] a b c) ;;nothing
 3557.94 ms   27.1%   3.7x  (let [[a b c] v] a b c) ;;length
 3287.01 ms   25.1%   4.0x  (let [[a b c] v] a b c) ;;tag +length
13116.50 ms  100.0%   1.0x  (let [[a b c] [1 2 3]] a b c)
 3287.39 ms   25.1%   4.0x  (let [a (v 0) b (v 1) c (v 2)] a b c)
 3615.61 ms   27.6%   3.6x  (let [a (nth v 0) b (nth v 1) c (nth v 2)]
a b c)
10634.64 ms   81.1%   1.2x  (let [a (first v) b (second v) c (first
(rest (rest v)))] a b c)
10147.02 ms   77.4%   1.3x  (let [x (first v) r1 (rest v) y (first r1)
r2 (rest r1) z (first r2)] x y z)
 8476.63 ms   64.6%   1.5x  (let [[a b c] lst] a b c)
 7106.42 ms   54.2%   1.8x  (let [a (nth lst 0) b (nth lst 1) c (nth
lst 2)] a b c)
 8172.17 ms   62.3%   1.6x  (let [a (first lst) b (second lst) c
(first (rest (rest lst)))] a b c)
 8031.60 ms   61.2%   1.6x  (let [x (first lst) r1 (rest lst) y (first
r1) r2 (rest r1) z (first r2)] x y z)

I'll post the rather small tweaks in a response to this post. Please
note that i was playing with a (month old) alpha 1.1 version of
clojure.core, so I am not sure what the differences would be between
it and a current version. I think the most interesting part is that
you can get pretty much the same effect by just telling it to inline
the 3ary version of nth. (Although it is a multi function, so it seems
there is still some sort of funcall overhead).
--~--~-~--~~~---~--~~
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
-~--~~~~--~-

Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-08 Thread Frantisek Sodomka

So far it seems that vectors win in Clojure:

(timings 3e5
  (let [v (vector 1 2 3) a (nth v 0) b (nth v 1) c (nth v 2)] (+ a b
c))
  (let [lst (list 1 2 3) a (nth lst 0) b (nth lst 1) c (nth lst 2)] (+
a b c)))

=>
  680.63 ms   83.6%   1.2x  (let [v (vector 1 2 3) a (nth v 0) b (nth
v 1) c (nth v 2)] (+ a b c))
  813.79 ms  100.0%   1.0x  (let [lst (list 1 2 3) a (nth lst 0) b
(nth lst 1) c (nth lst 2)] (+ a b c))

Frantisek

On Jul 8, 7:29 pm, John Harrop  wrote:
> Interesting. How are these timings affected if you add in the time taken to
> pack the list or vector in the first place, though? I have the feeling it
> may be slightly cheaper to unpack a vector, but noticeably cheaper to pack a
> list...
--~--~-~--~~~---~--~~
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
-~--~~~~--~~--~--~---



Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-08 Thread John Harrop
Interesting. How are these timings affected if you add in the time taken to
pack the list or vector in the first place, though? I have the feeling it
may be slightly cheaper to unpack a vector, but noticeably cheaper to pack a
list...

--~--~-~--~~~---~--~~
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
-~--~~~~--~~--~--~---



Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-08 Thread Frantisek Sodomka

To compare timings, I use this macro:

(defmacro time-ms [n expr]
  `(let [start# (. System (nanoTime))]
(dotimes [i# ~n]
  ~expr)
(/ (double (- (. System (nanoTime)) start#)) 100.0)))

(defmacro timings [n & expr]
  (let [expr-are-not-equal (cons 'not= expr)
expr-times (cons 'vector (map #(list 'time-ms n %) expr))]
`(if ~expr-are-not-equal
  (do (println "Expressions:\n")
  (dorun (map prn '~expr))
  (println "\nare NOT equal!"))
  (let [ts# ~expr-times
max-time# (apply max ts#)]
(dorun (map (fn [~'t ~'e] (printf "%8.2f ms %6.1f%% %5.1fx  "
~'t (* 100.0 (/ ~'t max-time#)) (/ max-time# ~'t))
  (prn ~'e))
 ts# '~expr))

Use is:
user=> (timings 1e6 (+ 2 4 5) (+ 2 (+ 4 5)))
  727.50 ms  100.0%   1.0x  (+ 2 4 5)
  353.18 ms   48.5%   2.1x  (+ 2 (+ 4 5))

For our case:
(let [v [1 2 3] lst '(1 2 3)]
  (timings 1e5
(let [[a b c] v] [a b c])
(let [a (v 0) b (v 1) c (v 2)] [a b c])
(let [a (nth v 0) b (nth v 1) c (nth v 2)] [a b c])
(let [a (first v) b (second v) c (first (rest (rest v)))] [a b c])
(let [x (first v) r1 (rest v) y (first r1) r2 (rest r1) z (first
r2)] [x y z])

(let [[a b c] lst] [a b c])
;; (let [a (lst 0) b (lst 1) c (lst 2)] [a b c])
(let [a (nth lst 0) b (nth lst 1) c (nth lst 2)] [a b c])
(let [a (first lst) b (second lst) c (first (rest (rest lst)))] [a
b c])
(let [x (first lst) r1 (rest lst) y (first r1) r2 (rest r1) z
(first r2)] [x y z])))

And on my computer I get these numbers:
  145.86 ms   64.5%   1.6x  (let [[a b c] v] [a b c])
   97.37 ms   43.1%   2.3x  (let [a (v 0) b (v 1) c (v 2)] [a b c])
   94.66 ms   41.9%   2.4x  (let [a (nth v 0) b (nth v 1) c (nth v 2)]
[a b c])
  226.15 ms  100.0%   1.0x  (let [a (first v) b (second v) c (first
(rest (rest v)))] [a b c])
  205.05 ms   90.7%   1.1x  (let [x (first v) r1 (rest v) y (first r1)
r2 (rest r1) z (first r2)] [x y z])
  219.42 ms   97.0%   1.0x  (let [[a b c] lst] [a b c])
  171.32 ms   75.8%   1.3x  (let [a (nth lst 0) b (nth lst 1) c (nth
lst 2)] [a b c])
  179.03 ms   79.2%   1.3x  (let [a (first lst) b (second lst) c
(first (rest (rest lst)))] [a b c])
  170.41 ms   75.4%   1.3x  (let [x (first lst) r1 (rest lst) y (first
r1) r2 (rest r1) z (first r2)] [x y z])

Frantisek


On Jul 8, 9:53 am, John Harrop  wrote:
> On Wed, Jul 8, 2009 at 3:42 AM, Frantisek Sodomka wrote:
>
>
>
> > If result is a vector v, then from these 4 cases:
> > (let [v [1 2 3]]
> >  (let [[a b c] v] a b c)
> >  (let [a (v 0) b (v 1) c (v 2)] a b c)
> >  (let [a (nth v 0) b (nth v 1) c (nth v 2)] a b c)
> >  (let [x (first v) r1 (rest v) y (first r1) r2 (rest r1) z (first
> > r2)] x y z))
>
> > using 'nth'
> > (let [a (nth v 0) b (nth v 1) c (nth v 2)] a b c)
> > is the fastest.
>
> Is it faster than using list and manually destructuring the list?
>
> If so, that would mean that (nth v n) is faster than (get v n), which would
> be odd since the latter should turn into the former anyway and be inlinable
> by the JIT.
--~--~-~--~~~---~--~~
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
-~--~~~~--~~--~--~---



Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-08 Thread John Harrop
On Wed, Jul 8, 2009 at 3:42 AM, Frantisek Sodomka wrote:

>
> If result is a vector v, then from these 4 cases:
> (let [v [1 2 3]]
>  (let [[a b c] v] a b c)
>  (let [a (v 0) b (v 1) c (v 2)] a b c)
>  (let [a (nth v 0) b (nth v 1) c (nth v 2)] a b c)
>  (let [x (first v) r1 (rest v) y (first r1) r2 (rest r1) z (first
> r2)] x y z))
>
> using 'nth'
> (let [a (nth v 0) b (nth v 1) c (nth v 2)] a b c)
> is the fastest.
>

Is it faster than using list and manually destructuring the list?

If so, that would mean that (nth v n) is faster than (get v n), which would
be odd since the latter should turn into the former anyway and be inlinable
by the JIT.

--~--~-~--~~~---~--~~
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
-~--~~~~--~~--~--~---



Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-08 Thread Frantisek Sodomka

If result is a vector v, then from these 4 cases:
(let [v [1 2 3]]
  (let [[a b c] v] a b c)
  (let [a (v 0) b (v 1) c (v 2)] a b c)
  (let [a (nth v 0) b (nth v 1) c (nth v 2)] a b c)
  (let [x (first v) r1 (rest v) y (first r1) r2 (rest r1) z (first
r2)] x y z))

using 'nth'
(let [a (nth v 0) b (nth v 1) c (nth v 2)] a b c)
is the fastest.

Frantisek

On Jul 7, 11:10 pm, John Harrop  wrote:
> Problem: Passing primitives from an inner loop to an outer loop efficiently.
> Here is what I've found.
>
> The fastest method of result batching, amazingly, is to pass out a list and:
>
> (let [foo (loop ... )
> x (double (first foo))
> r1 (rest foo)
> y (double (first r1))
> r2 (rest r1)
> z (double (first r2))] ... )
>
> (here passing out three doubles).
>
> About half as fast is:
>
> (let [[x y z] (loop ... )] ... ) with (double x), (double y), and (double z)
> used later in place of x y z and only one occurrence of each (so no more
> (double foo) conversions than before). The destructuring bind apparently
> exerts a higher run-time overhead compared to manual destructuring.
>
> (with-locals [x (double 0) y (double 0) z (double 0)]
> (loop ... )
> (do-something-with (double (var-get x)) (double (var-get y)) (double
> (var-get z
>
> is two full orders of magnitude slower (here the loop doesn't return a list
> but uses var-set).
>
> Using atoms is even worse.
>
> (let [#^doubles xyz (double-array (int 3))]
> (loop ... )
> (do-something-with (aget xyz (int 0)) (aget xyz (int 1)) (aget xyz (int
> 2
>
> with the loop using aset-double to pass out values is, surprisingly, no
> faster. Using plain aset makes no difference, as does removing the (int ...)
> wrappings around the numbers.
>
> Intermediate in speed is using a clojure vector to pass out values, e.g.
> [result-x result-y result-z] is the expression that returns a value from the
> loop, and
>
> (do-something-with (get results (int 0)) (get results (int 1)) (get
> results (int 2)))
>
> It's surprising that this is faster than using a primitive Java array with
> type-hints, and interesting that retrieving sequential items from a list is
> faster than a like number of in-order indexed retrievals from a vector,
> which could theoretically be optimized to a pointer-walk with each retrieval
> involving an increment and a dereference and a store, rather than an
> increment, three dereferences, and a store. (Dereference linked list entry
> item pointer, store, increment pointer, dereference to get next-entry
> pointer, dereference again, repeat.)
>
> Whatever, these results may be useful to someone, along with:
>
> (defn- extract-result-list-into [conv hint gensyms result-generator] (let
> [rs (take (count gensyms) (repeatedly gensym))] (into [(first rs)
> result-generator] (loop [r rs g (if hint (map #(with-meta % {:tag hint})
> gensyms) gensyms) output []] (if (empty? r) output (let [fr (first r) rr
> (rest r) frr (first rr)] (recur (rest r) (rest g) (into output (concat
> [(first g) (if conv `(~conv (first ~fr)) `(first ~fr))] (if frr [frr `(rest
> ~fr)]))
>
> This helper function can be used in a macro to suck the contents of a list
> into variables with conversions, type hints, or both; conv would be a symbol
> like `double, hint something like BigInteger, gensyms some gensyms (or other
> symbols) for the variable names (in order), and result-generator some code
> (e.g. resulting from a backtick expression). The output is a vector
> resembling
> [G__5877 (loop ... whatever code)
> #^hint G__5874 (conv (first G__5877))
> G_5878 (rest G__5877)
> #^hint G__5875 (conv (first G__5878))
> G_5879 (rest G__5878)
> #^hint G__5876 (conv (first G__5879))]
> which can be used in a let, loop, or other construct that uses bindings with
> the usual clojure syntax, and can even have other things added to it by
> macro code.
>
> The downside is relative inflexibility. If you pass in a gensyms seq with an
> embedded vector of two gensyms you'll get a valid destructuring bind for a
> composite item in the results, but it won't work with type hints or
> conversions, nor can those be mixed. Making it more sophisticated would be
> possible, though.
>
> This arose when I had a performance-critical double-barreled loop to
> optimize, and found that the outer loop was spending several thousand
> iterations of the inner loop worth of time just to extract the results via
> with-locals. I did some experimenting and benchmarking of various ways to
> get the output of the inner loop to code in the outer loop, using
> System/nanoTime, millions of repetitions, and averaging to determine the
> winner.
>
> An efficient "labeled recur" would be nice for clojure 2.0. :) (Limited
> though it would be to when the inner loop was in tail position in the outer
> loop.)
--~--~-~--~~~---~--~~
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 post

Re: Passing primitives from an inner loop to an outer loop efficiently

2009-07-07 Thread Jonathan Smith



On Jul 7, 5:10 pm, John Harrop  wrote:
> Problem: Passing primitives from an inner loop to an outer loop efficiently.
> Here is what I've found.
>
> The fastest method of result batching, amazingly, is to pass out a list and:
>
> (let [foo (loop ... )
> x (double (first foo))
> r1 (rest foo)
> y (double (first r1))
> r2 (rest r1)
> z (double (first r2))] ... )
>

This isn't suprising if you think about it.
In the list situation you are just grabbing the next pointer,
in an array situation you have to do math to access a vector.


> (here passing out three doubles).
>
> About half as fast is:
>
> (let [[x y z] (loop ... )] ... ) with (double x), (double y), and (double z)
> used later in place of x y z and only one occurrence of each (so no more
> (double foo) conversions than before). The destructuring bind apparently
> exerts a higher run-time overhead compared to manual destructuring.
>
If you look at the source code this isn't that surprising.
The big difference between (for example):
(let [[x y z] [1 2 3]] ...) and
(let [v [1 2 3] x (v 0) y (v 1) z (v 2)] ...)

Most of the slowdown Seems to have to do with bounds checking:
The first is 'safe' and returns nil for out of bounds, the second
throws an exception.

but
If you look at the source the 2ary version of nth gets inlined and the
3ary version (which destructuring bind expands to) does not.

and
if you put a symbol in the second position you'll note that
Destructuring bind rebinds the symbol with a Gensym even though it
cannot suffer from multiple evaluation.

If you look here:
http://gnuvince.wordpress.com/2009/05/11/clojure-performance-tips/
And look at 'avoid destructuring binding for vectors'

I did some hacking and improved the worst case from about 500ms on my
computer to 300ms.

(By inlining the 3ary version and removing the duplication; compiling
a destructuring function without bounds checking breaks compilation
for obvious reasons...). There are probably better ways to optimize it
as this was just me screwing around ('What can i break this
evening?').

I will note that there is something very funny going on with the
destructuring/binding example in the link above. The fast version on
my machine returns every number of times(1e7 1e8 1e9 etc...) returns
26 ms. It is kind of like it all just got collapsed into a constant.
--~--~-~--~~~---~--~~
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
-~--~~~~--~~--~--~---