This repository has been archived by the owner on Jul 23, 2019. It is now read-only.
Optimize cursor movement and selection manipulation #45
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This pull request contains a few changes that will optimize selection manipulation and cursor movement. I'd recommend reviewing it on a per-commit basis:
Anchor::offset
as an offset into the fragmentFragmentId
's vector of identifiers into anRc
FragmentId::{min, max}_value
I have tested the above optimizations following these steps:
Note how the time spent in
TextEditor
(i.e., the call toEditor::move_right
) has been reduced by a 35x factor. Performance is similar when moving cursors left, up or down.Next Steps
We think this is a good first improvement that will allow xray to stay well under the frame budget, even when the user is manipulating thousands of selections at the same time. We are currently focusing on other areas of the system and, therefore, we are thinking to put further improvements to this on hold until more architectural concerns have been sorted out.
In the future we may revisit some areas of this code to optimize it even further, for example:
Arc
. This reduces the chances of having the data we need ready to consume in the cache during a tree traversal. As such, it might be interesting to explore using different representations forNode
andFragmentId
so that they are more cache-friendly.Vec
to represent a stack of seen nodes during a traversal. While a single vector allocation is innocuous, doing one of them for each selection means we will repeatedly allocate a value on the heap and free it shortly afterward. By using the same (pool of) cursor(s) over and over again, we will minimize memory allocations, which we've learned to be very expensive on hot code paths like this. One implementation thought for this could be to instantiate a cursor during the initialization of a buffer, wrapping it in aArc
. Then, we could hand out clones of thatArc
and, in order to use the cursor, callers will have to invokemake_mut
on it. If the cursor is used by someone else, it will just be copied and only then we will pay for a heap allocation.FragmentId
comparisons. When resolving the position of an anchor we may end up comparing many fragment identifiers. Right now the size of a fragment id isn't known at compile time, so the number of comparison operations is not constant. We can partially solve this by using a more cache-friendly representation like suggested above, but constant factors may still be relatively high. As such, another idea would be to explore using SIMD instructions to compare two arbitrary fragment ids. This StackOverflow thread gives some insights on how we could do that./cc: @nathansobo