Repository navigation
Keep skipping unchanged children in the differ when a Text's string changes - #59373
Open
hryhoriiK97 wants to merge 2 commits into
Open
hryhoriiK97 wants to merge 2 commits into
hryhoriiK97 wants to merge 2 commits into
Conversation
With skipUnchangedChildrenInDifferentiator on, the side-by-side slicing gives up when a child's tag changes. React gives a Text a new raw text node whenever its string changes, and on Android a Paragraph doesn't form a stacking context, so the differ reaches that node while slicing the Paragraph's parent and slices all of the parent's children in full. A replaced leaf that doesn't form a view has no mount index, so both nodes can be listed as they are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary:
Follow-up to #59242, behind the same
skipUnchangedChildrenInDifferentiatorfeature flag (defaultfalse).Current behavior. The side-by-side slicing from #59242 gives up, and the differ slices both revisions in full, when a child's tag differs between them. React gives a
Texta new raw text node, with a new tag, every time its string changes. On Android,ParagraphShadowNodeunsetsFormsStackingContext, so a Paragraph's children are sliced together with the children of its parent. When a text inside a list changes, the slicing reaches the new raw text node while it slices the list's container, and gives up for the whole container. In the RNTester screen below (aScrollViewwith 200 rows, where 20 values change every 100 ms and a clock text changes every 16 ms), every commit fell back to slicing all ~1,400 pairs, so the flag made no difference on Android.With this change. When the tags (or the order indexes) differ, but both children are leaves that don't form a view, both are added to their lists as they are and the slicing continues. Such a node has no mount index, so it doesn't shift the views around it, and slicing in full lists it the same way. A replaced child that has children or forms a view still makes the slicing give up.
On iOS, #59242 already gives the full gain on this screen (~730 → ~193 µs per diff), and this change doesn't add to it. There a Paragraph forms a stacking context, so its raw text is reached only when the differ diffs the Paragraph itself, which has a single child.
Changelog:
[Internal]
Test Plan:
clang-formatreports no changes for the C++ file. Prettier and ESLint pass for the test file, andyarn flow-checkreports no errors in it.Mounting-itest.js: aTextwhose string changes next to unchanged children (only the Paragraph is updated), and an emptyViewremounted with a newkeynext to unchanged children (no mutations). A temporary log confirmed that both reach the new branch with the flag on.yarn fantom Mounting-itest: 64 passed (16 tests × 4 flag combinations). The full suite (FANTOM_FORCE_CI_MODE=1 yarn fantom) passes with the flag off, and with its default switched totruelocally so that every test runs with it on: 3,337 passed and 108 skipped both times.assembleRelease, arm64-v8a) on a Pixel 9a (Android 16), compiled withcmd package compile -m speed. The three arms are the same binary with a local switch (a system property read in C++, not part of this PR), launched in the order flag off, Skip unchanged children when slicing in the differ #59242, this PR, this PR, Skip unchanged children when slicing in the differ #59242, flag off, flag off, Skip unchanged children when slicing in the differ #59242, this PR. Each launch records a 30 s Perfetto trace with the C++ trace sections from Emit C++ TraceSection markers through ATrace on Android #59065.ScrollView, 20 values changed every 100 ms and a clock text every 16 ms (~1,250 commits per launch, 3 launches per arm)calculateShadowViewMutationsShadowTree::commitRuntimeScheduler::runEventLoop)calculateShadowViewMutationsThe feed renders a larger window of rows in some launches, so launches are compared with the same amount of work. The new branch ran once in the 1,800 feed commits checked above, so the ±3% between #59242 and this PR is noise; an earlier round gave 539 and 544 ms.
xcodebuildin Release on a physical iPhone 15 Pro, with the three arms switched by launch environment, timing every call ofcalculateShadowViewMutations(2,100 diffs per launch): flag off ~730 µs per diff (3 launches), Skip unchanged children when slicing in the differ #59242 ~193 µs (3), this PR ~190 µs (6). The flag already works on iOS, and this PR doesn't change it.🤖 Generated with Claude Code