Skip to content

Commit a224e03

Browse files
javachemeta-codesync[bot]
authored andcommitted
Add Fantom benchmark for ViewProps cloneProps (#57324)
Summary: Pull Request resolved: #57324 Adds `ViewProps-benchmark-itest.js` covering three scenarios that exercise the C++ Props construction path: 1. Mount N views with a full prop bag (initial Props construction). 2. Re-render N views with a full prop swap (cloneProps, every prop changes). 3. Re-render N views with a single-prop delta (cloneProps, hot path for typical UI updates). `fantom_flags enableCppPropsIteratorSetter:*` matrix-expands the suite so each scenario runs once with the classic per-field parse and once with the iterator-setter path. This establishes a baseline for upcoming changes that simplify the per-class `flag ? sourceProps.X : convertRawProp(...)` ternaries into two distinct constructors and that skip the now-unused `RawPropsParser::parse()` in the iterator-setter branch. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D109563268 fbshipit-source-id: e0d9cdbe81a0d62a20b0cc67c888e23c5d28b421
1 parent f9ba09d commit a224e03

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @fantom_flags enableCppPropsIteratorSetter:*
8+
* @flow strict-local
9+
* @format
10+
*/
11+
12+
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
13+
14+
import * as Fantom from '@react-native/fantom';
15+
import * as React from 'react';
16+
import {View} from 'react-native';
17+
18+
let root;
19+
let initialTree: React.MixedElement;
20+
let updatedTree: React.MixedElement;
21+
22+
function buildViewsWithFullPropBag(
23+
count: number,
24+
variant: 'a' | 'b',
25+
): React.MixedElement {
26+
const accent = variant === 'a' ? 'blue' : 'red';
27+
const offset = variant === 'a' ? 0 : 1;
28+
const children = [];
29+
for (let i = 0; i < count; i++) {
30+
children.push(
31+
<View
32+
key={i}
33+
accessibilityLabelledBy={`label-${i.toString()}`}
34+
accessibilityLiveRegion="polite"
35+
aria-labelledby={`label-${i.toString()}`}
36+
aria-live="polite"
37+
collapsable={false}
38+
focusable={i % 2 === 0}
39+
hasTVPreferredFocus={i % 2 === 0}
40+
id={String(i)}
41+
importantForAccessibility="no-hide-descendants"
42+
nativeID={String(i)}
43+
nextFocusDown={i + offset}
44+
nextFocusForward={i + 1 + offset}
45+
nextFocusLeft={i + 2 + offset}
46+
nextFocusRight={i + 3 + offset}
47+
nextFocusUp={i + 4 + offset}
48+
renderToHardwareTextureAndroid={true}
49+
style={{
50+
width: i + 1 + offset,
51+
height: i + 1 + offset,
52+
backgroundColor: accent,
53+
borderCurve: 'circular',
54+
borderBottomWidth: i % 2 === 0 ? 0 : 1,
55+
borderWidth: i % 2 === 0 ? 0 : 1,
56+
borderEndWidth: i % 2 === 0 ? 0 : 1,
57+
borderLeftWidth: i % 2 === 0 ? 0 : 1,
58+
borderRightWidth: i % 2 === 0 ? 0 : 1,
59+
borderStartWidth: i % 2 === 0 ? 0 : 1,
60+
borderTopWidth: i % 2 === 0 ? 0 : 1,
61+
borderBottomColor: accent,
62+
borderEndColor: accent,
63+
borderStartColor: accent,
64+
borderTopColor: accent,
65+
borderRightColor: accent,
66+
borderLeftColor: accent,
67+
opacity: i % 2 === 0 ? 0 : 1,
68+
elevation: i % 2 === 0 ? 0 : 1,
69+
pointerEvents: 'auto',
70+
boxShadow: '0 0 0 1px ' + accent,
71+
isolation: 'isolate',
72+
cursor: 'pointer',
73+
backfaceVisibility: 'visible',
74+
mixBlendMode: 'multiply',
75+
}}
76+
tabIndex={i % 2 === 0 ? 0 : undefined}
77+
/>,
78+
);
79+
}
80+
return (
81+
<View collapsable={false} nativeID="root">
82+
{children}
83+
</View>
84+
);
85+
}
86+
87+
function buildViewsWithSinglePropDelta(
88+
count: number,
89+
toggle: boolean,
90+
): React.MixedElement {
91+
const children = [];
92+
for (let i = 0; i < count; i++) {
93+
children.push(
94+
<View
95+
key={i}
96+
collapsable={false}
97+
nativeID={String(i)}
98+
style={{
99+
width: i + 1,
100+
height: i + 1,
101+
opacity: toggle && i % 2 === 0 ? 0.5 : 1,
102+
}}
103+
/>,
104+
);
105+
}
106+
return (
107+
<View collapsable={false} nativeID="root">
108+
{children}
109+
</View>
110+
);
111+
}
112+
113+
Fantom.unstable_benchmark
114+
.suite('Props construction')
115+
.test.each(
116+
[100, 1000],
117+
n => `mount ${n.toString()} views with full prop bag`,
118+
() => {
119+
Fantom.runTask(() => root.render(initialTree));
120+
},
121+
n => ({
122+
beforeAll: () => {
123+
initialTree = buildViewsWithFullPropBag(n, 'a');
124+
},
125+
beforeEach: () => {
126+
root = Fantom.createRoot();
127+
},
128+
afterEach: () => {
129+
root.destroy();
130+
},
131+
}),
132+
)
133+
.test.each(
134+
[100, 1000],
135+
n => `re-render ${n.toString()} views with full prop swap (cloneProps)`,
136+
() => {
137+
Fantom.runTask(() => root.render(updatedTree));
138+
},
139+
n => ({
140+
beforeAll: () => {
141+
initialTree = buildViewsWithFullPropBag(n, 'a');
142+
updatedTree = buildViewsWithFullPropBag(n, 'b');
143+
},
144+
beforeEach: () => {
145+
root = Fantom.createRoot();
146+
Fantom.runTask(() => root.render(initialTree));
147+
},
148+
afterEach: () => {
149+
root.destroy();
150+
},
151+
}),
152+
)
153+
.test.each(
154+
[100, 1000],
155+
n => `re-render ${n.toString()} views with single-prop delta (cloneProps)`,
156+
() => {
157+
Fantom.runTask(() => root.render(updatedTree));
158+
},
159+
n => ({
160+
beforeAll: () => {
161+
initialTree = buildViewsWithSinglePropDelta(n, false);
162+
updatedTree = buildViewsWithSinglePropDelta(n, true);
163+
},
164+
beforeEach: () => {
165+
root = Fantom.createRoot();
166+
Fantom.runTask(() => root.render(initialTree));
167+
},
168+
afterEach: () => {
169+
root.destroy();
170+
},
171+
}),
172+
);

0 commit comments

Comments
 (0)