-
-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Perf enhancement in function bodies #415
Comments
@joinr Note that
That change broke some tests. |
Ah, good catch. It's safer on keywords. Also would fail for strings. I don't think that's a big loss though, although analyzing the params to drop the need for that check would be nice. |
I tried a vector (indexed) based approach in the loop that binds param names to runtime args. I didn't really help. (defn parse-fn-args+body
[^clojure.lang.Associative ctx interpret eval-do*
{:sci.impl/keys [fixed-arity var-arg-name ^clojure.lang.Indexed params body] :as _m}
fn-name macro? with-meta?]
(let [min-var-args-arity (when var-arg-name fixed-arity)
body-count (count body)
param-count (count params)
return (if (= 1 body-count)
(let [fst (first body)]
#(interpret % fst))
#(eval-do* % body))
f (fn run-fn [& args]
(let [;; tried making bindings a transient, but saw no perf improvement (see #246)
bindings (.get ^java.util.Map ctx :bindings)
arg-count (count args)
;;max-idx (dec arg-count)
args ^clojure.lang.Indexed (vec args)
bindings
(loop [idx 0
ret bindings]
(if (< idx param-count)
(let [fp (try (.nth params idx)
(catch Exception _
(throw-arity fn-name macro? args)))]
(if (= '& fp)
(assoc ret (try (.nth params (inc idx))
(catch Exception _e
(throw-arity fn-name macro? args)))
(subvec args idx))
(recur (inc idx)
(assoc ret fp (try (.nth args idx)
(catch Exception _e
(throw-arity fn-name macro? args)))))))
(do
(when (> arg-count idx)
(throw-arity fn-name macro? args))
ret)))
ctx (#?(:clj .assoc
:cljs -assoc) ctx :bindings bindings)
ret (return ctx)
;; m (meta ret)
recur? (instance? Recur ret)]
(if recur?
(let [recur-val (t/getVal ret)]
(if min-var-args-arity
(let [[fixed-args [rest-args]]
[(subvec recur-val 0 min-var-args-arity)
(subvec recur-val min-var-args-arity)]]
(recur (into fixed-args rest-args)))
(recur recur-val)))
ret)))]
(if with-meta?
(with-meta
f
(if min-var-args-arity
{:sci.impl/min-var-args-arity min-var-args-arity}
{:sci.impl/fixed-arity fixed-arity}))
f))) |
@joinr I think I applied most of your patches to the |
Some thoughts:
|
regard 1., I think that's good. 2 is interesting. I was wondering if we actually needed a persistent map for bindings, or if that was there for convenience. If not, then it's plausible that you follow more standard stack frame stuff like you mentioned. That would likely be faster. There's probably quit a bit of improvement in just pushing args into the right place efficiently. |
Yes, the bindings map is just convenience, no other reason. |
Note: some of these ideas may incur obvious portability concerns. I have a myopic bias toward jvm/native-image in this case; I'm not really concerned with cljs. That could be a broader consideration as well (might have missing analogues in cljs). |
I think using a mutable map / stack in JS is of no concern since it's single threaded. |
@joinr I made changes to
This is not only due to changes in fns.cljc, but there were additional performance enhancements made. What I mainly did in fns.cljc: Pre-create a per-arity function (with known params because the arity is known) that uses |
Very nice. Might be able to check to see if direct method dispatch to a hinted .nth gets you even more. |
@joinr Indeed, I'm using:
to inline these things. |
See joinr@a542ab2.
Master
JVM
GraalVM native-image:
Patch
TODO:
JVM (Faster than master)
GraalVM native-image (not significantly faster than master)
cc @joinr
a542ab2fb30e54f117607273339794d5add42f49.patch.txt
The text was updated successfully, but these errors were encountered: