mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add benchmark to compare rendering times for View and ViewNativeComponent (#53248)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53248 Changelog: [internal] This adds a new benchmark that compares the rendering time (only rendering, not committing, mounting, effects, etc.) of `<View>` and `<ViewNativeComponent>`. Baseline: | (index) | Task name | Latency avg (ns) | Latency med (ns) | Throughput avg (ops/s) | Throughput med (ops/s) | Samples | | ------- | ---------------------------------------- | ----------------- | ------------------ | ---------------------- | ---------------------- | ------- | | 0 | 'render 100 views (Noop)' | '333036 ± 0.39%' | '328452 ± 2393.0' | '3019 ± 0.18%' | '3045 ± 22' | 3003 | | 1 | 'render 100 views (ViewNativeComponent)' | '1335974 ± 3.45%' | '1228468 ± 7541.5' | '797 ± 0.71%' | '814 ± 5' | 1000 | | 2 | 'render 100 views (View)' | '2296988 ± 1.60%' | '2170821 ± 12374' | '449 ± 0.74%' | '461 ± 3' | 1000 | This shows that **`<View>` currently has an overhead of 75% in rendering time**. I've also tested a modification of `View` such as: ``` component View(...props: ViewProps) { return { // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // Built-in properties that belong on the element type: ViewNativeComponent, key: undefined, // $FlowExpectedError[prop-missing] ref: props.ref, props, }; } ``` This makes `View` basically a no-op component, and the benchmark after this looks like: | (index) | Task name | Latency avg (ns) | Latency med (ns) | Throughput avg (ops/s) | Throughput med (ops/s) | Samples | | ------- | ---------------------------------------- | ----------------- | ----------------- | ---------------------- | ---------------------- | ------- | | 0 | 'render 100 views (View)' | '1743010 ± 2.25%' | '1630816 ± 10616' | '600 ± 0.74%' | '613 ± 4' | 1000 | | 1 | 'render 100 views (ViewNativeComponent)' | '1370699 ± 4.04%' | '1242284 ± 14172' | '789 ± 0.74%' | '805 ± 9' | 1000 | This shows that `View`, just for existing as a wrapper component, has an overhead of 31% in rendering time, which means that **the opportunities to reduce the overhead beyond what we already did are limited**. Reviewed By: rshest Differential Revision: D80169514 fbshipit-source-id: aa2a1fc3f9d0ee3a60c03dd32555802fa7265251
This commit is contained in:
1 parent
e8bf982bac
commit
d009a02c6c
2 files changed
+106
No files matched your search
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
|
||||
|
||||
import measureRenderTime from '../../../../src/private/__tests__/utilities/measureRenderTime';
|
||||
import ViewNativeComponent from '../ViewNativeComponent';
|
||||
import * as Fantom from '@react-native/fantom';
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
|
||||
let root;
|
||||
let testViews: React.MixedElement;
|
||||
|
||||
const NUMBER_OF_VIEWS = 100;
|
||||
const NUMBER_OF_ITERATIONS = 1000;
|
||||
|
||||
component Noop(children: React.Node, style?: mixed) {
|
||||
return children;
|
||||
}
|
||||
|
||||
Noop.displayName = 'Noop';
|
||||
|
||||
Fantom.unstable_benchmark
|
||||
.suite('View vs. ViewNativeComponent', {minIterations: NUMBER_OF_ITERATIONS})
|
||||
.test.each(
|
||||
[Noop, ViewNativeComponent, View],
|
||||
Component =>
|
||||
`render ${NUMBER_OF_VIEWS} views (${Component.displayName ?? Component.name ?? 'ViewNativeComponent'})`,
|
||||
() => {
|
||||
return {
|
||||
overriddenDuration: measureRenderTime(root, testViews),
|
||||
};
|
||||
},
|
||||
Component => ({
|
||||
beforeAll: () => {
|
||||
let views: React.Node = null;
|
||||
for (let i = 0; i < NUMBER_OF_VIEWS; i++) {
|
||||
views = (
|
||||
<Component style={{width: i + 1, height: i + 1}}>{views}</Component>
|
||||
);
|
||||
}
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
testViews = views;
|
||||
},
|
||||
beforeEach: () => {
|
||||
root = Fantom.createRoot();
|
||||
},
|
||||
afterEach: () => {
|
||||
root.destroy();
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import * as Fantom from '@react-native/fantom';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
const now = Fantom.unstable_benchmark.now;
|
||||
|
||||
export default function measureRenderTime(
|
||||
root: Fantom.Root,
|
||||
elements: React.MixedElement,
|
||||
): number {
|
||||
let startTime: ?number;
|
||||
let endTime: ?number;
|
||||
|
||||
function RecordStart() {
|
||||
startTime ??= now();
|
||||
return null;
|
||||
}
|
||||
|
||||
function RecordEnd() {
|
||||
endTime = now();
|
||||
return null;
|
||||
}
|
||||
|
||||
Fantom.runTask(() => {
|
||||
// React renders nodes using a depth-first algorithm.
|
||||
root.render(
|
||||
<>
|
||||
<RecordStart />
|
||||
{elements}
|
||||
<RecordEnd />
|
||||
</>,
|
||||
);
|
||||
});
|
||||
|
||||
return nullthrows(endTime) - nullthrows(startTime);
|
||||
}
|
||||
Reference in new issue
Block a user