-
-
Notifications
You must be signed in to change notification settings - Fork 864
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
semantic-tokens: cache modifier lookups #3670
semantic-tokens: cache modifier lookups #3670
Conversation
c/c @sebastiansturm |
Hi @alanz, thanks for your PR!
The results of 5 consecutive evaluations of
One obvious pessimization (that you already remarked on in a comment, but AFAICS didn't address yet) is that empty modifier face sets will fail the |
Thanks for taking a look @sebastiansturm . I am actually using it from my https://github.com/alanz/lsp-mode/tree/semantic-modifiers-optimise branch, which is basically this PR on top of #3668, which fully populates the modifier faces for rust-analyzer. And I put the optimisation in pace because otherwise navigating in rust files such as rust-analyzer |
I just did those benchmarks on my own machine with emacs 28.1, jit enabled, without precompiling lsp-rust.el or lsp-semantic-tokens.el. I get
My Messages was littered with missing font declarations, so I changed lsp-rust.el to have (defun lsp-rust-analyzer--set-tokens ()
"Set the mapping between rust-analyzer keywords and fonts to apply.
The keywords are sent in the initialize response, in the semantic
tokens legend."
(setq lsp-semantic-token-modifier-faces
'(
("documentation" . font-lock-nop-face)
("declaration" . font-lock-nop-face)
("definition" . font-lock-nop-face)
("static" . font-lock-nop-face)
("abstract" . font-lock-nop-face)
("deprecated" . font-lock-nop-face)
("readonly" . font-lock-nop-face)
("default_library" . font-lock-nop-face)
("async" . font-lock-nop-face)
("attribute" . font-lock-nop-face)
("callable" . font-lock-nop-face)
("constant" . font-lock-nop-face)
("consuming" . font-lock-nop-face)
("control_flow" . font-lock-nop-face)
("crate_root" . font-lock-nop-face)
("injected" . font-lock-nop-face)
("intra_doc_link" . font-lock-nop-face)
("library" . font-lock-nop-face)
("mutable" . font-lock-mutable-modifier-face)
("public" . font-lock-nop-face)
("reference" . font-lock-reference-modifier-face)
("trait" . font-lock-nop-face)
("unsafe" . font-lock-nop-face)
))) Which shut them up, but made no difference to the performance. So for me it makes a big difference to use this PR |
I revisited this tonight, and redid my benchmarks. And realised my first result in #3670 (comment) is bogus. Herewith revised benchmark results
The change I made was to supply a symbol for the not-found state in the hashmap lookup to distinguish between not cached and no fonts applicable. And with that it goes roughly twice as fast as without it. |
thanks! Makes sense that caching would help with that long list of modifiers, and might be a good idea in general, now that the no-modifiers case is no longer being pessimized. Adding a few inline comments now... |
we will not have the full range of possible usages, hence a | ||
tractable hash map. | ||
|
||
This is set as buffer-local. It should probably be shared in a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I guess it would make sense to store the modifiers cache as part of the workspace, cf. lsp--semantic-tokens-initialize-workspace
(which initializes lsp--workspace-semantic-tokens-faces
and lsp--workspace-semantic-tokens-modifier-faces
depending on token capabilities reported by the server)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am leaving this as a follow-up, given that the current implementation looks for whichever first random client has a value. Updating it could be tricky:
Line 476
(modifier-faces
(when lsp-semantic-tokens-apply-modifiers
(seq-some #'lsp--workspace-semantic-tokens-modifier-faces lsp--buffer-workspaces)))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true, at some point I should probably also permit using several semhl-capable lsp servers simultaneously; though I wonder how useful that is in practice (does anyone use several servers at once, with more than one of them providing semantic highlights?)
lsp-semantic-tokens.el
Outdated
(push (aref modifier-faces j) faces-to-apply))) | ||
;; What if there are no faces? we need to cache that fact. | ||
(puthash modifier-code faces-to-apply semantic-token-modifier-cache)) | ||
(mapc (lambda (face) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a bit faster than mapc + lambda (could you please benchmark this?):
(dolist (face faces-to-apply) (add-face-text-property text-property-beg text-property-end face))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes a miniscule difference
Benchmark, on collector.rs
(benchmark/fontification 50)
My improved caching, on top of PR 3668
(8.577711427999999 43 2.15895247)
(8.576204198000001 43 2.1617159800000003)
(8.585730497000000 43 2.1770649470000007)
With dolist
(8.706144393999999 44 2.205544484)
(8.598737171000000 43 2.1419443979999997)
(8.361974631000000 41 2.0108028530000004)
(8.385988765000000 41 2.001014791000001)
I was wondering if we could do the combining of all those faces before putting them into the cache, so we only need to apply the combination once per token.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wondered about that too, but I didn't see anything akin to add-face-text-property
that would accept a font list (and setting the face property directly would conflict with other font-lock mechanisms that might be used besides semantic tokens). Maybe such a method does exist, or maybe it might be worthwhile to convert the face list into a face attribute plist which could then be passed to add-face-text-property
(though we'd then have to invalidate the cache any time a different theme is selected).
For now, I think the current implementation is fine and it seems to work as intended, so I'll approve this as-is, then we can merge #3680 and your pr #3668 on top of that
Make a buffer-local cache of the mapping from a modifier bitmap to the set of faces it resolves to. The assumption is that for a given language the modifier usage will tend to cluster, so the lookup table will not be large.
02df879
to
4e94bd2
Compare
Make a buffer-local cache of the mapping from a modifier bitmap to the
set of faces it resolves to.
The assumption is that for a given language the modifier usage will
tend to cluster, so the lookup table will not be large.