Skip to content

perf(treesitter): cache skip nodes while searching for a match - #454

Open
Sciencentistguy wants to merge 1 commit into
andymass:masterfrom
Sciencentistguy:perf/cache-treesitter-skips
Open

Sciencentistguy wants to merge 1 commit into
andymass:masterfrom
Sciencentistguy:perf/cache-treesitter-skips

Conversation

@Sciencentistguy

Copy link
Copy Markdown

% is slow in treesitter buffers. In a ~90 line rust function, % plus the matchparen redraw takes ~213 ms.

The skip expression runs for every candidate delimiter while searching for a match, and each call to syntax.get_skips() re-runs the matchup query over g:matchup_treesitter_stopline lines either side of the cursor to rebuild the same set of skip nodes. For that function that's 75 queries per %, and the matchparen highlight pays the same cost on every cursor movement.

get_skips() now keeps the last result and reuses it while the buffer's changedtick, the language and query at the cursor, and the queried rows around the cursor are all unchanged. The row calculation moves to internal.get_query_range() so get_matches() and the cache share it.

For that rust function, on my machine, Neovim 0.12.6, one % + redraw + cursor-move cycle goes from 212.7 ms on master to 25.6 ms with this change.

I added a test in test/new/test-treesitter/test.vim that counts query runs and checks the cache is reused and invalidated by moving out of the cached rows, editing the buffer and changing the query. It fails without the change. The test/new suites that run directly with nvim -u test.vim pass and luacheck is clean; I haven't run the vader suite, vint, or Neovim 0.11.2.

The skip expression is evaluated for every candidate delimiter while
searching for a match, and each evaluation ran the matchup query over
`g:matchup_treesitter_stopline` lines either side of the cursor to
rebuild the same set of skip nodes. With a `%` over a ~90 line rust
function this was 75 queries and ~150ms, which is also paid by the
matchparen highlight on every cursor movement.

Keep the last result and reuse it while the buffer's changedtick, the
language and query at the cursor and the rows around the cursor that
were queried are all unchanged.

Move the calculation of the queried rows into
`internal.get_query_range()` so both places agree on it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant