Repository navigation
perf(virtualized-lists): use a Set for stale-index lookup in ViewabilityHelper - #59590
seyedmostafahasani wants to merge 1 commit into
Conversation
…ityHelper _onUpdateSync filtered the indices to check with this._viewableIndices.includes(ii), which is O(n) per index and O(n^2) per update. Build a Set of the current viewable indices once and use has() instead. Behavior is unchanged.
|
Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
|
|
|
Thanks for the review, and agreed on the numbers. In my benchmark the Set only breaks even at around 50-100 viewable indices (122 ns vs 363 ns at n=10, so ~3x slower there), and with I'll try your suggestion of building the Set once in |
|
Thanks again for the review. I tried your suggestion and also looked at the call sites more closely. Measured on the real While reading the code I noticed the filter in Instead of the Set, I'd change this PR to skip the filter when the input is the stored array: if (viewableIndicesToCheck !== this._viewableIndices) {
viewableIndicesToCheck = viewableIndicesToCheck.filter(ii =>
this._viewableIndices.includes(ii),
);
}Behavior stays identical for any other caller, and all 22 existing ViewabilityHelper tests pass. Measured speedup vs. current main: ~1.0-1.1x at 10-50 viewable items (within noise), ~1.2x at ~200, ~1.6x at ~800. So the win only matters for very large viewable sets. If you'd prefer removing the dead filter entirely, or think this isn't worth changing, I'm happy to do that or close the PR instead. |
Summary:
ViewabilityHelper._onUpdateSyncfiltered the indices to check withthis._viewableIndices.includes(ii), which is O(n) per index and therefore O(n^2) per update. This builds aSetof the current viewable indices once and useshas()for the lookup instead. Behavior is unchanged.Changelog:
[INTERNAL] [CHANGED] - Use a Set for the stale-index lookup in ViewabilityHelper
Test Plan:
No behavior change. The existing
ViewabilityHelper-test.jscases cover this path, in particular "minimumViewTime ignores stale viewability updates". Run withyarn jest packages/virtualized-lists/Lists.Benchmark:
Micro-benchmark of the filter alone (Node v22; median of 9 interleaved rounds per size; before/after results verified identical). Typical case: every checked index is still viewable.
When half of the checked indices have scrolled out of view, the gap is larger (for example 2.5x at n=100 and 97x at n=10,000), because each miss makes
includes()scan the whole array.Note: n is the number of items viewable at once, not the list length. The break-even point is roughly 50-100 indices. Below that, building the
Setcosts more thanincludes(), so for lists with only a few dozen viewable items this change is neutral to slightly slower. The gain shows up when many items are viewable at once, for example very small items, large windows, or largewindowSize/viewAreaCoveragePercentThresholdsetups. This is a micro-benchmark of the filter only, not an end-to-end scroll measurement.