-
Notifications
You must be signed in to change notification settings - Fork 120
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
reformat-form performance issues #226
Comments
Hi @ericdallo, I do realize you are compiling clojure-lsp to a binary executable with GraalVM's I'm not sure the best way to approach this, but it seems sensible to first try to reproduce the performance issue you are encountering. My dev box is an aging but capable late 2013 iMac, with 32gb of RAM and a 3.5 GHz Quad-Core i7 and SSD storage. I am exploring under the cljfmt dir on cljfmt master. I've added a {:paths ["src" "resources"]
:deps {org.clojure/clojure {:mvn/version "1.10.3"}
rewrite-clj/rewrite-clj {:mvn/version "1.0.605-alpha" }
criterium/criterium {:mvn/version "0.4.6"}}} And a (ns perf
(:require [cljfmt.core :as cljfmt]
[criterium.core :as criterium]
[rewrite-clj.parser :as p]))
(def sample1 "https://raw.githubusercontent.com/clojure-lsp/clojure-lsp/cd64d430d83cb068d4a0a011beeded3beb791201/src/clojure_lsp/handlers.clj")
(defn -main [[test-id]]
(case test-id
"reformat-string"
(let [s (slurp sample1)]
(println "benchmarking reformat-string")
(criterium/with-progress-reporting (criterium/bench (cljfmt/reformat-string s) :verbose)))
"reformat-form"
(let [s (slurp sample1)
n (p/parse-string-all s)]
(println "benchmarking reformat-form")
(criterium/with-progress-reporting (criterium/bench (cljfmt/reformat-form n) :verbose)))))
(-main *command-line-args*) A run of
And a run of
If we look at the mean times, So why does your I won't start to guess yet. First, forgetting GraalVM for now, I'll ask, do my little tests represent your usage of (I've commited the above work here: lread@4d16f43) |
Thank you for the help on this! Yes, it seems to be similar on how clojure-lsp calls it: https://github.com/clojure-lsp/clojure-lsp/blob/master/src/clojure_lsp/handlers.clj#L213 |
It appears to me that the main reason for the difference in time is that clojure-lsp's |
Last time I tested, the issue was on cljfmt call, even if clojure-lsp call on every top-level, it should not be at least equal to cljfmt format the whole buffer string I think |
Okay, I have done extensive testing and I believe the major culprit is cljfmt's (defn- find-namespace [zloc]
(some-> zloc top (z/find z/next ns-form?) z/down z/next z/sexpr)) This code appears to search the zipper for the ns form to get the name of the namespace. When the entire Clojure file is provided, that should be done quite quickly. Since In clojure-lsp's case, this is made significant by the fact that this code may be called thousands of times per file (depending on the number of forms). All of those 0–2ms calls build up. To address this, I find the following to be a plausible solution: run the I can prepare a fork later to demonstrate these changes if they sound sensical. |
Thanks for your work isolating this. Rather than add it as metadata, it would be better to pass it as an argument, or change the It's also worth memoizing the function and seeing what the performance gain is before we delve any further, in order to ensure that it's the culprit. |
I can confirm that memoizing This is what I have been using for testing: (def sample1 (slurp "https://raw.githubusercontent.com/clojure-lsp/clojure-lsp/cd64d430d83cb068d4a0a011beeded3beb791201/src/clojure_lsp/handlers.clj"))
(def forms (mapv (comp nforms/forms-node vector)
(:children (p/parse-string-all sample1) )))
(criterium/with-progress-reporting
(criterium/bench
(mapv (fn [form-node]
(cljfmt/reformat-form form-node {}))
forms))) Using
Using a memoized
Calculating the ns-name upfront and passing through:
I like the idea of swapping out the |
Here is a draft of how I imagine the fix: master...LuisThiamNye:luistn/fix-perf-find-namespace |
Thank you very much for the investigation @LuisThiamNye! @lread may be interested here |
Very cool! Nice sleuthing @LuisThiamNye! |
That looks fine, though I don't think we need to make the namespace finder function part of the public API. |
I've refactored the |
I think I missed the merge 😅, is this available in latest release already @weavejester ? |
Just release 0.8.0. |
Thanks! I'll bump clojure-lsp |
I noticed the performance improvements! |
it works great! thanks both for the help! |
This is a separate issue regarding the performance issues mentioned on #212.
Context:
On clojure-lsp we use cljfmt to 2 LSP features:
reformat-form
with the buffer as string)reformat-form
).The issue seems that reformat-form can be very slow on medium/big forms/buffers affecting the parenthesis manipulation performance for users like here.
I also noticed that calling
reformat-string
on the same range ofreformat-form
is faster.Example:
calling
reformat-string
on this whole buffer, takes ~450ms while callingreformat-form
on the same range (the whole buffer) will take ~2000ms.The text was updated successfully, but these errors were encountered: