Permalink
Browse files

Optimize pr-str by making more use of StringBuffer.

Previously, a huge number of temporary vectors and lazy sequences were
built up and concat-ed, which ran reasonably on newer JS engines (e.g.
V8) but suffered from miserable performance on old engines (such as
IE8).  By passing a StringBuffer down the callstack and appending to
that, performance gains of 10-100X can be seen in older JS engines.

For backwards compatibility, the new fastpath uses a new
IPrintWithWriter protocol, and the old IPrintable is left in place.
The old protocol is used as a fallback for items that do not satisfy
IPrintWithWriter.
  • Loading branch information...
emezeske authored and David Nolen committed Jul 26, 2012
1 parent b8fcdf0 commit c8bc05ca8e3c7e30a85148dd67398aa4d0e6b468
Showing with 333 additions and 50 deletions.
  1. +21 −9 src/clj/cljs/core.clj
  2. +256 −40 src/cljs/cljs/core.cljs
  3. +56 −1 test/cljs/cljs/core_test.cljs
@@ -154,8 +154,8 @@
'[IFn ICounted IEmptyableCollection ICollection IIndexed ASeq ISeq INext
ILookup IAssociative IMap IMapEntry ISet IStack IVector IDeref
IDerefWithTimeout IMeta IWithMeta IReduce IKVReduce IEquiv IHash
ISeqable ISequential IList IRecord IReversible ISorted IPrintable
IPending IWatchable IEditableCollection ITransientCollection
ISeqable ISequential IList IRecord IReversible ISorted IPrintable IWriter
IPrintWithWriter IPending IWatchable IEditableCollection ITransientCollection
ITransientAssociative ITransientMap ITransientVector ITransientSet
IMultiFn])
(iterate (fn [[p b]]
@@ -574,12 +574,14 @@
(deftype* ~t ~fields ~pmasks)
(set! (.-cljs$lang$type ~t) true)
(set! (.-cljs$lang$ctorPrSeq ~t) (fn [this#] (list ~(core/str r))))
(set! (.-cljs$lang$ctorPrWriter ~t) (fn [this# writer#] (-write writer# ~(core/str r))))
(extend-type ~t ~@(dt->et impls fields true))
~t)
`(do
(deftype* ~t ~fields ~pmasks)
(set! (.-cljs$lang$type ~t) true)
(set! (.-cljs$lang$ctorPrSeq ~t) (fn [this#] (list ~(core/str r))))
(set! (.-cljs$lang$ctorPrWriter ~t) (fn [this# writer#] (-write writer# ~(core/str r))))
~t))))
(defn- emit-defrecord
@@ -633,19 +635,28 @@
'IMap
`(~'-dissoc [this# k#] (if (contains? #{~@(map keyword base-fields)} k#)
(dissoc (with-meta (into {} this#) ~'__meta) k#)
(new ~tagname ~@(remove #{'__extmap '__hash} fields)
(new ~tagname ~@(remove #{'__extmap '__hash} fields)
(not-empty (dissoc ~'__extmap k#))
nil)))
'ISeqable
`(~'-seq [this#] (seq (concat [~@(map #(list `vector (keyword %) %) base-fields)]
`(~'-seq [this#] (seq (concat [~@(map #(list `vector (keyword %) %) base-fields)]
~'__extmap)))
'IPrintable
`(~'-pr-seq [this# opts#]
(let [pr-pair# (fn [keyval#] (pr-sequential pr-seq "" " " "" opts# keyval#))]
(pr-sequential
pr-pair# (core/str "#" ~(core/str (namespace rname) "." (name rname)) "{") ", " "}" opts#
(concat [~@(map #(list `vector (keyword %) %) base-fields)]
(concat [~@(map #(list `vector (keyword %) %) base-fields)]
~'__extmap))))
'IPrintWithWriter
`(~'-pr-writer [this# writer# opts#]
(let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# pr-writer "" " " "" opts# keyval#))]
(pr-sequential-writer
writer# pr-pair# (core/str "#" ~(name rname) "{") ", " "}" opts#
(concat [~@(map #(list `vector (keyword %) %) base-fields)]
~'__extmap))))
])
[fpps pmasks] (prepare-protocol-masks env tagname impls)
protocols (collect-protocols impls env)
@@ -679,6 +690,7 @@
~(emit-defrecord &env rsym r fields impls)
(set! (.-cljs$lang$type ~r) true)
(set! (.-cljs$lang$ctorPrSeq ~r) (fn [this#] (list ~(core/str r))))
(set! (.-cljs$lang$ctorPrWriter ~r) (fn [this# writer#] (-write writer# ~(core/str r))))
~(build-positional-factory rsym r fields)
~(build-map-factory rsym r fields)
~r)))
@@ -942,7 +954,7 @@
[true `(do ~@body)]
(let [k (first exprs)
v (second exprs)
seqsym (when-not (keyword? k) (gensym))
recform (if (keyword? k) recform `(recur (next ~seqsym)))
steppair (step recform (nnext exprs))
@@ -991,8 +1003,8 @@
(defmacro amap
"Maps an expression across an array a, using an index named idx, and
return value named ret, initialized to a clone of a, then setting
each element of ret to the evaluation of expr, returning the new
return value named ret, initialized to a clone of a, then setting
each element of ret to the evaluation of expr, returning the new
array ret."
[a idx ret expr]
`(let [a# ~a
@@ -1006,7 +1018,7 @@
(defmacro areduce
"Reduces an expression across an array a, using an index named idx,
and return value named ret, initialized to init, setting ret to the
and return value named ret, initialized to init, setting ret to the
evaluation of expr at each step, returning ret."
[a idx ret init expr]
`(let [a# ~a]
Oops, something went wrong.

0 comments on commit c8bc05c

Please sign in to comment.