Skip to content

Commit 3f588c8

Browse files
rauhsswannodette
authored andcommitted
CLJS-2099: Optimize apply by avoiding .apply
Avoid javascripts .apply in apply function. Similar to apply-to now walks the passed sequence one by one with first and next and calls the function efficiently.
1 parent 723e1fc commit 3f588c8

File tree

2 files changed

+115
-53
lines changed

2 files changed

+115
-53
lines changed

src/main/cljs/cljs/core.cljs

Lines changed: 79 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3614,11 +3614,12 @@ reduces them without incurring seq initialization"
36143614

36153615
(defn spread
36163616
[arglist]
3617-
(cond
3618-
(nil? arglist) nil
3619-
(nil? (next arglist)) (seq (first arglist))
3620-
:else (cons (first arglist)
3621-
(spread (next arglist)))))
3617+
(when-not (nil? arglist)
3618+
(let [n (next arglist)]
3619+
(if (nil? n)
3620+
(seq (first arglist))
3621+
(cons (first arglist)
3622+
(spread n))))))
36223623

36233624
(defn concat
36243625
"Returns a lazy seq representing the concatenation of the elements in the supplied colls."
@@ -3729,52 +3730,89 @@ reduces them without incurring seq initialization"
37293730
(gen-apply-to)
37303731

37313732
(set! *unchecked-if* true)
3733+
3734+
(defn- apply-to-simple
3735+
"Internal. DO NOT USE!
3736+
Assumes args was already called with seq beforehand!"
3737+
([f ^seq args]
3738+
(if (nil? args)
3739+
(if (.-cljs$core$IFn$_invoke$arity$0 f)
3740+
(.cljs$core$IFn$_invoke$arity$0 f)
3741+
(.call f f))
3742+
(apply-to-simple f (-first args) (next args))))
3743+
([f a0 ^seq args]
3744+
(if (nil? args)
3745+
(if (.-cljs$core$IFn$_invoke$arity$1 f)
3746+
(.cljs$core$IFn$_invoke$arity$1 f a0)
3747+
(.call f f a0))
3748+
(apply-to-simple f a0 (-first args) (next args))))
3749+
([f a0 a1 ^seq args]
3750+
(if (nil? args)
3751+
(if (.-cljs$core$IFn$_invoke$arity$2 f)
3752+
(.cljs$core$IFn$_invoke$arity$2 f a0 a1)
3753+
(.call f f a0 a1))
3754+
(apply-to-simple f a0 a1 (-first args) (next args))))
3755+
([f a0 a1 a2 ^seq args]
3756+
(if (nil? args)
3757+
(if (.-cljs$core$IFn$_invoke$arity$3 f)
3758+
(.cljs$core$IFn$_invoke$arity$3 f a0 a1 a2)
3759+
(.call f f a0 a1 a2))
3760+
(apply-to-simple f a0 a1 a2 (-first args) (next args))))
3761+
([f a0 a1 a2 a3 ^seq args]
3762+
(if (nil? args)
3763+
(if (.-cljs$core$IFn$_invoke$arity$4 f)
3764+
(.cljs$core$IFn$_invoke$arity$4 f a0 a1 a2 a3)
3765+
(.call f f a0 a1 a2 a3))
3766+
(gen-apply-to-simple f 4 args))))
3767+
37323768
(defn apply
37333769
"Applies fn f to the argument list formed by prepending intervening arguments to args."
37343770
([f args]
3735-
(let [fixed-arity (.-cljs$lang$maxFixedArity f)]
3736-
(if (.-cljs$lang$applyTo f)
3737-
(let [bc (bounded-count (inc fixed-arity) args)]
3738-
(if (<= bc fixed-arity)
3739-
(apply-to f bc args)
3740-
(.cljs$lang$applyTo f args)))
3741-
(.apply f f (to-array args)))))
3771+
(if (.-cljs$lang$applyTo f)
3772+
(let [fixed-arity (.-cljs$lang$maxFixedArity f)
3773+
bc (bounded-count (inc fixed-arity) args)]
3774+
(if (<= bc fixed-arity)
3775+
(apply-to f bc args)
3776+
(.cljs$lang$applyTo f args)))
3777+
(apply-to-simple f (seq args))))
37423778
([f x args]
3779+
(if (.-cljs$lang$applyTo f)
37433780
(let [arglist (list* x args)
3744-
fixed-arity (.-cljs$lang$maxFixedArity f)]
3745-
(if (.-cljs$lang$applyTo f)
3746-
(let [bc (bounded-count (inc fixed-arity) arglist)]
3747-
(if (<= bc fixed-arity)
3748-
(apply-to f bc arglist)
3749-
(.cljs$lang$applyTo f arglist)))
3750-
(.apply f f (to-array arglist)))))
3781+
fixed-arity (.-cljs$lang$maxFixedArity f)
3782+
bc (inc (bounded-count fixed-arity args))]
3783+
(if (<= bc fixed-arity)
3784+
(apply-to f bc arglist)
3785+
(.cljs$lang$applyTo f arglist)))
3786+
(apply-to-simple f x (seq args))))
37513787
([f x y args]
3788+
(if (.-cljs$lang$applyTo f)
37523789
(let [arglist (list* x y args)
3753-
fixed-arity (.-cljs$lang$maxFixedArity f)]
3754-
(if (.-cljs$lang$applyTo f)
3755-
(let [bc (bounded-count (inc fixed-arity) arglist)]
3756-
(if (<= bc fixed-arity)
3757-
(apply-to f bc arglist)
3758-
(.cljs$lang$applyTo f arglist)))
3759-
(.apply f f (to-array arglist)))))
3790+
fixed-arity (.-cljs$lang$maxFixedArity f)
3791+
bc (+ 2 (bounded-count (dec fixed-arity) args))]
3792+
(if (<= bc fixed-arity)
3793+
(apply-to f bc arglist)
3794+
(.cljs$lang$applyTo f arglist)))
3795+
(apply-to-simple f x y (seq args))))
37603796
([f x y z args]
3797+
(if (.-cljs$lang$applyTo f)
37613798
(let [arglist (list* x y z args)
3762-
fixed-arity (.-cljs$lang$maxFixedArity f)]
3763-
(if (.-cljs$lang$applyTo f)
3764-
(let [bc (bounded-count (inc fixed-arity) arglist)]
3765-
(if (<= bc fixed-arity)
3766-
(apply-to f bc arglist)
3767-
(.cljs$lang$applyTo f arglist)))
3768-
(.apply f f (to-array arglist)))))
3799+
fixed-arity (.-cljs$lang$maxFixedArity f)
3800+
bc (+ 3 (bounded-count (- fixed-arity 2) args))]
3801+
(if (<= bc fixed-arity)
3802+
(apply-to f bc arglist)
3803+
(.cljs$lang$applyTo f arglist)))
3804+
(apply-to-simple f x y z (seq args))))
37693805
([f a b c d & args]
3770-
(let [arglist (cons a (cons b (cons c (cons d (spread args)))))
3771-
fixed-arity (.-cljs$lang$maxFixedArity f)]
3772-
(if (.-cljs$lang$applyTo f)
3773-
(let [bc (bounded-count (inc fixed-arity) arglist)]
3774-
(if (<= bc fixed-arity)
3775-
(apply-to f bc arglist)
3776-
(.cljs$lang$applyTo f arglist)))
3777-
(.apply f f (to-array arglist))))))
3806+
(if (.-cljs$lang$applyTo f)
3807+
(let [spread-args (spread args)
3808+
arglist (cons a (cons b (cons c (cons d spread-args))))
3809+
fixed-arity (.-cljs$lang$maxFixedArity f)
3810+
bc (+ 4 (bounded-count (- fixed-arity 3) spread-args))]
3811+
(if (<= bc fixed-arity)
3812+
(apply-to f bc arglist)
3813+
(.cljs$lang$applyTo f arglist)))
3814+
(apply-to-simple f a b c d (spread args)))))
3815+
37783816
(set! *unchecked-if* false)
37793817

37803818
(defn vary-meta

src/main/clojure/cljs/core.cljc

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
defcurried rfn specify! js-this this-as implements? array js-obj
4545
simple-benchmark gen-apply-to js-str es6-iterable load-file* undefined?
4646
specify copy-arguments goog-define js-comment js-inline-comment
47-
unsafe-cast require-macros use-macros])])
47+
unsafe-cast require-macros use-macros gen-apply-to-simple])])
4848
#?(:cljs (:require-macros [cljs.core :as core]
4949
[cljs.support :refer [assert-args]]))
5050
(:require clojure.walk
@@ -2668,17 +2668,13 @@
26682668
(core/defn- gen-apply-to-helper
26692669
([] (gen-apply-to-helper 1))
26702670
([n]
2671-
(core/let [prop (symbol (core/str "-cljs$core$IFn$_invoke$arity$" n))
2672-
f (symbol (core/str "cljs$core$IFn$_invoke$arity$" n))]
2673-
(if (core/<= n 20)
2674-
`(let [~(cs (core/dec n)) (-first ~'args)
2675-
~'args (-rest ~'args)]
2676-
(if (== ~'argc ~n)
2677-
(if (. ~'f ~prop)
2678-
(. ~'f (~f ~@(take n cs)))
2679-
(~'f ~@(take n cs)))
2680-
~(gen-apply-to-helper (core/inc n))))
2681-
`(throw (js/Error. "Only up to 20 arguments supported on functions"))))))
2671+
(if (core/<= n 20)
2672+
`(let [~(cs (core/dec n)) (-first ~'args)
2673+
~'args (-rest ~'args)]
2674+
(if (== ~'argc ~n)
2675+
(~'f ~@(take n cs))
2676+
~(gen-apply-to-helper (core/inc n))))
2677+
`(throw (js/Error. "Only up to 20 arguments supported on functions")))))
26822678

26832679
(core/defmacro gen-apply-to []
26842680
`(do
@@ -2690,6 +2686,34 @@
26902686
~(gen-apply-to-helper))))
26912687
(set! ~'*unchecked-if* false)))
26922688

2689+
(core/defn- gen-apply-to-simple-helper
2690+
[f num-args args]
2691+
(core/let [new-arg-sym (symbol (core/str "a" num-args))
2692+
proto-name (core/str "cljs$core$IFn$_invoke$arity$" (core/inc num-args))
2693+
proto-prop (symbol (core/str ".-" proto-name))
2694+
proto-inv (symbol (core/str "." proto-name))
2695+
next-sym (symbol (core/str "next_" num-args))
2696+
all-args (mapv #(symbol (core/str "a" %)) (range (core/inc num-args)))]
2697+
`(let [~new-arg-sym (cljs.core/-first ~args)
2698+
~next-sym (cljs.core/next ~args)]
2699+
(if (nil? ~next-sym)
2700+
(if (~proto-prop ~f)
2701+
(~proto-inv ~f ~@all-args)
2702+
(.call ~f ~f ~@all-args))
2703+
~(if (core/<= 19 num-args)
2704+
;; We've exhausted all protocols, fallback to .apply:
2705+
`(let [arr# (cljs.core/array ~@all-args)]
2706+
(loop [s# ~next-sym]
2707+
(when s#
2708+
(do (.push arr# (cljs.core/-first s#))
2709+
(recur (cljs.core/next s#)))))
2710+
(.apply ~f ~f arr#))
2711+
(gen-apply-to-simple-helper f (core/inc num-args) next-sym))))))
2712+
2713+
(core/defmacro gen-apply-to-simple
2714+
[f num-args args]
2715+
(gen-apply-to-simple-helper f num-args args))
2716+
26932717
(core/defmacro with-out-str
26942718
"Evaluates exprs in a context in which *print-fn* is bound to .append
26952719
on a fresh StringBuffer. Returns the string created by any nested

0 commit comments

Comments
 (0)