Skip to content

Commit

Permalink
Optimize pr-str by making more use of StringBuffer.
Browse files Browse the repository at this point in the history
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
Evan Mezeske authored and David Nolen committed Sep 5, 2012
1 parent b8fcdf0 commit c8bc05c
Show file tree
Hide file tree
Showing 3 changed files with 333 additions and 50 deletions.
30 changes: 21 additions & 9 deletions src/clj/cljs/core.clj
Expand Up @@ -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]]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand All @@ -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]
Expand Down

0 comments on commit c8bc05c

Please sign in to comment.