Use sparse array for cell position caches #1312
Merged
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 PR swaps out the hash based cell position caches and the underlying binary search with a constant-time sparse array linear layout vector implementation.
The vector stores element sizes in a single dimension. Sizes are stored in blocks with power-of-two length. The vector supports constant-time
(index) => position
lookup, update, insertion, and removal by shifting the index to find the block index, then a mask (mod) to find the cell index.Reverse lookup
(position) => index
is currently linear with respect to the number of blocks/size of each block. Flamegraphs indicate that on a table with 1MM rows and with the default block_size of 128, this amounts to about 0.15-1ms (150-1000 microseconds) per call when scrolling at the end of the list. If avoiding linear scans is an issue on principle, it would be possible to cache and invalidate the block sizes' prefix sum, and implement this lookup as a binary search again. The block size can also be intelligently tuned based on total element count for a classic space/time trade-off.This change means the following methods are now
O(1)
:getVisibleCellRange
isO(n)
, wheren=num_blocks
The biggest practical benefit is that cell dimensions are stable across invalidation. After initial measurement the scrollbars won't jump around, as you can see in this screen capture I uploaded to youtube. The first half is rendering a MultiGrid with
react-virtualized@9.21.0
, and the second half is rendering the same MultiGrid with the current PR. You can find the source for this demo here.npm test
) all passnpm run prettier
).npm run typecheck
).