Permalink
Browse files

CLJS-367: chunked seq support in for and doseq, comprehension benchmarks

Copy & paste from Clojure with small tweaks (-nth instead of .nth on
chunks, ClojureScript-specific type hints).

doseq maintains the style of ClojureScript's doseq while using looping
logic from Clojure.

Also fixes a minor bug around argument format verification in for.
  • Loading branch information...
1 parent 3d5c68f commit 44389e2f233ccc26ed5a1c75b1e043054e61b586 @michalmarczyk michalmarczyk committed with swannodette Apr 6, 2013
Showing with 85 additions and 15 deletions.
  1. +7 −0 benchmark/cljs/benchmark_runner.cljs
  2. +77 −14 src/clj/cljs/core.clj
  3. +1 −1 src/cljs/cljs/core.cljs
@@ -206,4 +206,11 @@
(simple-benchmark [r r] (last r) 1)
(println)
+(println ";;; comprehensions")
+(simple-benchmark [xs (range 512)] (last (for [x xs y xs] (+ x y))) 10)
+(simple-benchmark [xs (vec (range 512))] (last (for [x xs y xs] (+ x y))) 10)
+(simple-benchmark [a (Box. 0) xs (range 512)] (doseq [x xs y xs] (set! a -val (+ (.-val a) x))) 100)
+(simple-benchmark [a (Box. 0) xs (vec (range 512))] (doseq [x xs y xs] (set! a -val (+ (.-val a) x))) 100)
+(println)
+
(println "\n")
View
@@ -207,6 +207,11 @@
(defmacro coercive-= [x y]
(bool-expr (list 'js* "(~{} == ~{})" x y)))
+;; internal - do not use.
+(defmacro coercive-boolean [x]
+ (with-meta (list 'js* "~{}" x)
+ {:tag 'boolean}))
+
(defmacro true? [x]
(bool-expr (list 'js* "~{} === true" x)))
@@ -991,7 +996,7 @@
(conj (pop groups) (conj (peek groups) [k v]))
(conj groups [k v])))
[] (partition 2 seq-exprs)))
- err (fn [& msg] (throw (apply core/str msg)))
+ err (fn [& msg] (throw (ex-info (apply core/str msg) {})))
emit-bind (fn emit-bind [[[bind expr & mod-pairs]
& [[_ next-expr] :as next-groups]]]
(let [giter (gensym "iter__")
@@ -1012,11 +1017,49 @@
(recur (rest ~gxs))))
:else `(cons ~body-expr
(~giter (rest ~gxs)))))]
- `(fn ~giter [~gxs]
- (lazy-seq
- (loop [~gxs ~gxs]
- (when-first [~bind ~gxs]
- ~(do-mod mod-pairs)))))))]
+ (if next-groups
+ #_ "not the inner-most loop"
+ `(fn ~giter [~gxs]
+ (lazy-seq
+ (loop [~gxs ~gxs]
+ (when-first [~bind ~gxs]
+ ~(do-mod mod-pairs)))))
+ #_"inner-most loop"
+ (let [gi (gensym "i__")
+ gb (gensym "b__")
+ do-cmod (fn do-cmod [[[k v :as pair] & etc]]
+ (cond
+ (= k :let) `(let ~v ~(do-cmod etc))
+ (= k :while) `(when ~v ~(do-cmod etc))
+ (= k :when) `(if ~v
+ ~(do-cmod etc)
+ (recur
+ (unchecked-inc ~gi)))
+ (keyword? k)
+ (err "Invalid 'for' keyword " k)
+ :else
+ `(do (chunk-append ~gb ~body-expr)
+ (recur (unchecked-inc ~gi)))))]
+ `(fn ~giter [~gxs]
+ (lazy-seq
+ (loop [~gxs ~gxs]
+ (when-let [~gxs (seq ~gxs)]
+ (if (chunked-seq? ~gxs)
+ (let [c# ^not-native (chunk-first ~gxs)
+ size# (count c#)
+ ~gb (chunk-buffer size#)]
+ (if (coercive-boolean
+ (loop [~gi 0]
+ (if (< ~gi size#)
+ (let [~bind (-nth c# ~gi)]
+ ~(do-cmod mod-pairs))
+ true)))
+ (chunk-cons
+ (chunk ~gb)
+ (~giter (chunk-rest ~gxs)))
+ (chunk-cons (chunk ~gb) nil)))
+ (let [~bind (first ~gxs)]
+ ~(do-mod mod-pairs)))))))))))]
`(let [iter# ~(emit-bind (to-groups seq-exprs))]
(iter# ~(second seq-exprs)))))
@@ -1028,14 +1071,15 @@
(assert-args doseq
(vector? seq-exprs) "a vector for its binding"
(even? (count seq-exprs)) "an even number of forms in binding vector")
- (let [step (fn step [recform exprs]
+ (let [err (fn [& msg] (throw (ex-info (apply core/str msg) {})))
+ step (fn step [recform exprs]
(if-not exprs
[true `(do ~@body)]
(let [k (first exprs)
v (second exprs)
- seqsym (when-not (keyword? k) (gensym))
- recform (if (keyword? k) recform `(recur (next ~seqsym)))
+ seqsym (gensym "seq__")
+ recform (if (keyword? k) recform `(recur (next ~seqsym) nil 0 0))
steppair (step recform (nnext exprs))
needrec (steppair 0)
subform (steppair 1)]
@@ -1049,11 +1093,30 @@
~subform
~@(when needrec [recform]))
~recform)]
- :else [true `(loop [~seqsym (seq ~v)]
- (when ~seqsym
- (let [~k (first ~seqsym)]
- ~subform
- ~@(when needrec [recform]))))]))))]
+ (keyword? k) (err "Invalid 'doseq' keyword" k)
+ :else (let [chunksym (with-meta (gensym "chunk__")
+ {:tag 'not-native})
+ countsym (gensym "count__")
+ isym (gensym "i__")
+ recform-chunk `(recur ~seqsym ~chunksym ~countsym (unchecked-inc ~isym))
+ steppair-chunk (step recform-chunk (nnext exprs))
+ subform-chunk (steppair-chunk 1)]
+ [true `(loop [~seqsym (seq ~v)
+ ~chunksym nil
+ ~countsym 0
+ ~isym 0]
+ (if (coercive-boolean (< ~isym ~countsym))
+ (let [~k (-nth ~chunksym ~isym)]
+ ~subform-chunk
+ ~@(when needrec [recform-chunk]))
+ (when-let [~seqsym (seq ~seqsym)]
+ (if (chunked-seq? ~seqsym)
+ (let [c# (chunk-first ~seqsym)]
+ (recur (chunk-rest ~seqsym) c#
+ (count c#) 0))
+ (let [~k (first ~seqsym)]
+ ~subform
+ ~@(when needrec [recform]))))))])))))]
(nth (step nil (seq seq-exprs)) 1)))
(defmacro array [& rest]
View
@@ -81,7 +81,7 @@
argv as arguments"}
*main-cli-fn* nil)
-(declare pr-str)
+(declare pr-str chunk-first chunk-rest)
(defn type [x]
(when-not (nil? x)

0 comments on commit 44389e2

Please sign in to comment.