Repository navigation
Commit 392b764
authored
Avoid per-child dynamic_casts during Yoga layout
## Summary:
`YogaLayoutableShadowNode` runs three `dynamic_cast`s for children on every layout pass:
- `resolveErrata` checks `dynamic_cast<const LayoutConformanceShadowNode *>(this)`. `configureYogaTree` calls it for every child to decide whether the child has to be configured again, so it runs on every layout, and it fails for almost every node, which is the slowest case of a `dynamic_cast`.
- `getContentBounds` checks `dynamic_cast<const ViewShadowNode *>` for every child to read its `hitSlop`. It fails for every child that isn't a `View` (Text, ScrollView, Image, …).
- `shadowNodeFromContext` gets the shadow node back from a Yoga node's context with a `dynamic_cast`. It runs for every child in `layout()` and `getContentBounds()`, and in Yoga's measure, baseline and clone callbacks.
**Current behavior.** In Time Profiler traces of JS-driven updates on an iPhone, `__dynamic_cast` took 11–18% of the JS thread, most of it inside `layoutTree`, and the type comparisons in those traces go through `strcmp`.
**With this change.** `LayoutConformanceShadowNode` and `ViewShadowNode` are both `final`, and their nodes are only created by descriptors that report the class's own `Handle()`, so comparing `getComponentHandle()` with that handle gives the same answer as the `dynamic_cast`; a `static_cast` follows. The Yoga node's context is only set by this class's two constructors, to `this`, so `shadowNodeFromContext` recovers it with a `static_cast` of the stored pointer. The behavior doesn't change.
## Changelog:
[GENERAL] [CHANGED] - Yoga layout no longer uses `dynamic_cast` for every child to check its type
## Test Plan:
- The pinned `clang-format` passes on the changed file, and it compiles for iOS (arm64).
- `yarn fantom` (full suite, tester rebuilt with cmake from this change, then from `main`): no test fails with this change that doesn't also fail on `main` in the same local environment. The local failures (LogBox and `expect` snapshot formatting, `Text-itest`, `EventTimingAPI-itest`) happen with and without this change.
- **Measurement.** RNTester built with `xcodebuild` in Release and run on a physical iPhone 15 Pro (iOS 26.6.1). Both arms are the same binary, with this change switched by an environment variable read once. 8 launches in the order before, after, after, before, after, before, before, after; in each, 12 s of Time Profiler per scenario, counting JS-thread samples. Interactions were driven by [Argent](https://github.com/software-mansion/argent) on the physical iPhone. The phone warmed up during the runs, so the totals of the same arm vary by up to 30%; the time in `dynamic_cast` is the most direct number, and the medians are given for context.
RNTester Playground, 30 `Text`s animated with `Animated` (JS driver), medians of 4 launches per arm:
| Scenario | | before | this PR | change |
|---|---|--:|--:|--:|
| Color animated | `dynamic_cast` inside layout, per 12 s | 317 ms | 1.5 ms | −99.5% |
| | `dynamic_cast`, share of the JS thread | 17.0% | 4.9% | |
| | Yoga layout (`layoutTree`), per 12 s | 1,144 ms | 836 ms | −26.9% |
| | JS thread, per 12 s | 2,454 ms | 2,154 ms | −12.2% |
| Color and font size animated | `dynamic_cast` inside layout, per 12 s | 338 ms | 1.5 ms | −99.6% |
| | `dynamic_cast`, share of the JS thread | 11.1% | 3.0% | |
| | Yoga layout (`layoutTree`), per 12 s | 2,618 ms | 2,166 ms | −17.3% |
| | JS thread, per 12 s | 4,098 ms | 3,562 ms | −13.1% |
The `dynamic_cast` time left with this change (about 3–5% of the JS thread) comes from other places, such as `adoptYogaChild` and the shadow node constructors, which this PR doesn't change.
On Android these casts cost much less, since there the type comparison compares pointers: in the same color-animation scenario on an Android emulator, `dynamic_cast` inside layout went from 1.5% to 0.2% of the JS thread.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Differential Revision: D128421875
Pull Request resolved: #596431 parent 97f6997 commit 392b764
8 files changed
Lines changed: 110 additions & 10 deletions
File tree
- packages/react-native
- Libraries/Components/LayoutConformance/__tests__
- ReactCommon/react/renderer/components/view
- private/react-native-fantom/tester/src/stubs
- scripts/cxx-api/api-snapshots
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
560 | 560 | | |
561 | 561 | | |
562 | 562 | | |
563 | | - | |
564 | | - | |
565 | | - | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
566 | 572 | | |
567 | 573 | | |
568 | 574 | | |
| |||
829 | 835 | | |
830 | 836 | | |
831 | 837 | | |
832 | | - | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
833 | 844 | | |
834 | 845 | | |
835 | 846 | | |
| |||
951 | 962 | | |
952 | 963 | | |
953 | 964 | | |
954 | | - | |
955 | | - | |
| 965 | + | |
| 966 | + | |
956 | 967 | | |
957 | 968 | | |
958 | 969 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| 31 | + | |
30 | 32 | | |
31 | 33 | | |
32 | 34 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
376 | 376 | | |
377 | 377 | | |
378 | 378 | | |
| 379 | + | |
379 | 380 | | |
380 | 381 | | |
381 | 382 | | |
| |||
386 | 387 | | |
387 | 388 | | |
388 | 389 | | |
389 | | - | |
390 | 390 | | |
391 | 391 | | |
392 | 392 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3186 | 3186 | | |
3187 | 3187 | | |
3188 | 3188 | | |
| 3189 | + | |
3189 | 3190 | | |
3190 | 3191 | | |
3191 | 3192 | | |
| |||
3197 | 3198 | | |
3198 | 3199 | | |
3199 | 3200 | | |
3200 | | - | |
3201 | 3201 | | |
3202 | 3202 | | |
3203 | 3203 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
| 76 | + | |
76 | 77 | | |
77 | 78 | | |
78 | 79 | | |
| |||
83 | 84 | | |
84 | 85 | | |
85 | 86 | | |
86 | | - | |
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| |||
0 commit comments