From 2632895ed296f846f65d00c8c99c3493594ba946 Mon Sep 17 00:00:00 2001 From: Abhijeet Jha <74712637+iamAbhi-916@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:17:06 +0530 Subject: [PATCH 01/11] add native perf benchmarking infrastructure for Fabric components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit measures rendering pipeline — JS reconciliation → Fabric → Yoga layout → Composition commit → frame Missing Components: Button, Modal, Pressable, TouchableHighlight, TouchableOpacity, SectionList. --- .github/workflows/perf-tests.yml | 11 +- .../src/config/thresholdPresets.ts | 10 + .../NativePerfBenchmarkExample.js | 228 +++++++++++++ .../src/js/utils/RNTesterList.windows.js | 5 + .../jest.native-perf.config.js | 71 ++++ packages/e2e-test-app-fabric/package.json | 5 +- .../test/NativePerfHelpers.ts | 112 ++++++ .../core/View.native-perf-test.ts | 78 +++++ ...iew.native-perf-test.ts.perf-baseline.json | 320 ++++++++++++++++++ 9 files changed, 838 insertions(+), 2 deletions(-) create mode 100644 packages/@react-native-windows/tester/src/js/examples-win/NativePerfBenchmark/NativePerfBenchmarkExample.js create mode 100644 packages/e2e-test-app-fabric/jest.native-perf.config.js create mode 100644 packages/e2e-test-app-fabric/test/NativePerfHelpers.ts create mode 100644 packages/e2e-test-app-fabric/test/__native_perf__/core/View.native-perf-test.ts create mode 100644 packages/e2e-test-app-fabric/test/__native_perf__/core/__perf_snapshots__/View.native-perf-test.ts.perf-baseline.json diff --git a/.github/workflows/perf-tests.yml b/.github/workflows/perf-tests.yml index d29a71a3290..19f36f67b18 100644 --- a/.github/workflows/perf-tests.yml +++ b/.github/workflows/perf-tests.yml @@ -61,7 +61,14 @@ jobs: RN_TARGET_PLATFORM: windows run: yarn perf:ci continue-on-error: true # Don't fail here — let comparison decide - + - name: Run native perf tests + id: native-perf-run + working-directory: packages/e2e-test-app-fabric + env: + CI: 'true' + RN_TARGET_PLATFORM: windows + run: yarn perf:native:ci + continue-on-error: true # ── Compare & Report ─────────────────────────────────── - name: Compare against baselines id: compare @@ -80,7 +87,9 @@ jobs: name: perf-results path: | packages/e2e-test-app-fabric/.perf-results/ + packages/e2e-test-app-fabric/.native-perf-results/ packages/e2e-test-app-fabric/test/__perf__/**/__perf_snapshots__/ + packages/e2e-test-app-fabric/test/__native_perf__/**/__perf_snapshots__/ retention-days: 30 # ── Status Gate ──────────────────────────────────────── diff --git a/packages/@react-native-windows/perf-testing/src/config/thresholdPresets.ts b/packages/@react-native-windows/perf-testing/src/config/thresholdPresets.ts index 3b1b260c488..02d6880c104 100644 --- a/packages/@react-native-windows/perf-testing/src/config/thresholdPresets.ts +++ b/packages/@react-native-windows/perf-testing/src/config/thresholdPresets.ts @@ -56,4 +56,14 @@ export const ThresholdPresets: Readonly< maxCV: 0.6, mode: 'track', }, + + native: { + maxDurationIncrease: 15, + maxDuration: Infinity, + minAbsoluteDelta: 5, + maxRenderCount: 1, + minRuns: 10, + maxCV: 0.5, + mode: 'gate', + }, }; diff --git a/packages/@react-native-windows/tester/src/js/examples-win/NativePerfBenchmark/NativePerfBenchmarkExample.js b/packages/@react-native-windows/tester/src/js/examples-win/NativePerfBenchmark/NativePerfBenchmarkExample.js new file mode 100644 index 00000000000..dab42e7b432 --- /dev/null +++ b/packages/@react-native-windows/tester/src/js/examples-win/NativePerfBenchmark/NativePerfBenchmarkExample.js @@ -0,0 +1,228 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + */ + +'use strict'; + +const React = require('react'); +const { + View, + Text, + TextInput, + Image, + ScrollView, + FlatList, + Switch, + ActivityIndicator, + Pressable, + StyleSheet, +} = require('react-native'); + +const {useState, useRef, useCallback, useEffect} = React; + +const PHASE_IDLE = 'idle'; +const PHASE_CLEARING = 'clearing'; +const PHASE_MOUNTING = 'mounting'; +const PHASE_DONE = 'done'; + +const COMPONENT_REGISTRY = { + View: () => , + Text: () => Benchmark Text, + TextInput: () => ( + + ), + Image: () => ( + + ), + ScrollView: () => ( + + {Array.from({length: 20}, (_, i) => ( + + ))} + + ), + FlatList: () => ( + ({key: String(i)}))} + renderItem={({item}) => {item.key}} + /> + ), + Switch: () => , + ActivityIndicator: () => , +}; + +function BenchmarkRunner() { + const [componentName, setComponentName] = useState('View'); + const [runsInput, setRunsInput] = useState('15'); + const [phase, setPhase] = useState(PHASE_IDLE); + const [showTarget, setShowTarget] = useState(false); + const [resultsJson, setResultsJson] = useState(''); + + const durationsRef = useRef([]); + const runIndexRef = useRef(0); + const totalRunsRef = useRef(15); + const markNameRef = useRef(''); + + const finishRun = useCallback(() => { + const markEnd = `perf-end-${runIndexRef.current}`; + performance.mark(markEnd); + try { + const measure = performance.measure( + `perf-run-${runIndexRef.current}`, + markNameRef.current, + markEnd, + ); + durationsRef.current.push(measure.duration); + } catch (_) {} + performance.clearMarks(markNameRef.current); + performance.clearMarks(markEnd); + performance.clearMeasures(`perf-run-${runIndexRef.current}`); + + runIndexRef.current++; + if (runIndexRef.current < totalRunsRef.current) { + setPhase(PHASE_CLEARING); + } else { + setShowTarget(false); + setResultsJson( + JSON.stringify({ + componentName, + runs: durationsRef.current.length, + durations: durationsRef.current, + }), + ); + setPhase(PHASE_DONE); + } + }, [componentName]); + + useEffect(() => { + if (phase === PHASE_CLEARING) { + setShowTarget(false); + requestAnimationFrame(() => { + setPhase(PHASE_MOUNTING); + }); + } + }, [phase]); + + useEffect(() => { + if (phase === PHASE_MOUNTING) { + const markStart = `perf-start-${runIndexRef.current}`; + markNameRef.current = markStart; + performance.mark(markStart); + setShowTarget(true); + } + }, [phase]); + + useEffect(() => { + if (phase === PHASE_MOUNTING && showTarget) { + requestAnimationFrame(() => { + finishRun(); + }); + } + }, [phase, showTarget, finishRun]); + + const handleRun = useCallback(() => { + const runs = parseInt(runsInput, 10) || 15; + totalRunsRef.current = runs; + runIndexRef.current = 0; + durationsRef.current = []; + setResultsJson(''); + setPhase(PHASE_CLEARING); + }, [runsInput]); + + const ComponentFactory = COMPONENT_REGISTRY[componentName]; + + return ( + + + + + + Run Benchmark + + + + + {phase} + + + + {showTarget && ComponentFactory ? : null} + + + + {resultsJson} + + + ); +} + +const styles = StyleSheet.create({ + container: {flex: 1, padding: 8}, + controls: {flexDirection: 'row', gap: 8, marginBottom: 8}, + input: { + borderWidth: 1, + borderColor: '#ccc', + padding: 6, + minWidth: 100, + fontSize: 14, + }, + button: { + backgroundColor: '#0078D4', + paddingHorizontal: 16, + paddingVertical: 8, + borderRadius: 4, + justifyContent: 'center', + }, + buttonText: {color: 'white', fontWeight: 'bold'}, + status: {fontSize: 12, color: '#666', marginBottom: 4}, + targetContainer: { + minHeight: 100, + borderWidth: 1, + borderColor: '#eee', + marginBottom: 8, + }, + target: {width: 80, height: 80, backgroundColor: '#f0f0f0'}, + targetInput: {width: 200, height: 40, borderWidth: 1, borderColor: '#999'}, + targetImage: {width: 80, height: 80}, + scrollItem: {height: 20, backgroundColor: '#ddd', marginBottom: 2}, + results: {fontSize: 10, fontFamily: 'monospace', color: '#333'}, +}); + +exports.displayName = 'NativePerfBenchmarkExample'; +exports.framework = 'React'; +exports.category = 'Basic'; +exports.title = 'Native Perf Benchmark'; +exports.description = + 'Measures native rendering pipeline via performance.mark/measure.'; + +exports.examples = [ + { + title: 'Benchmark Runner', + render: function () { + return ; + }, + }, +]; diff --git a/packages/@react-native-windows/tester/src/js/utils/RNTesterList.windows.js b/packages/@react-native-windows/tester/src/js/utils/RNTesterList.windows.js index c9c15047b6c..8237d6778af 100644 --- a/packages/@react-native-windows/tester/src/js/utils/RNTesterList.windows.js +++ b/packages/@react-native-windows/tester/src/js/utils/RNTesterList.windows.js @@ -212,6 +212,11 @@ const Components: Array = [ category: 'Basic', module: require('../examples/Performance/PerformanceComparisonExample'), }, + { + key: 'NativePerfBenchmark', + category: 'Basic', + module: require('../examples-win/NativePerfBenchmark/NativePerfBenchmarkExample'), + }, ...RNTesterListFbInternal.Components, ]; diff --git a/packages/e2e-test-app-fabric/jest.native-perf.config.js b/packages/e2e-test-app-fabric/jest.native-perf.config.js new file mode 100644 index 00000000000..0b31ca9fb5e --- /dev/null +++ b/packages/e2e-test-app-fabric/jest.native-perf.config.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + * @ts-check + */ + +const assetTransform = 'react-native-windows/jest/assetFileTransformer.js'; + +module.exports = { + preset: '@rnx-kit/jest-preset', + + roots: ['/test/'], + testMatch: ['**/__native_perf__/**/*.native-perf-test.{ts,tsx}'], + + testEnvironment: '@react-native-windows/automation', + + testTimeout: 180000, + + maxWorkers: 1, + + setupFilesAfterEnv: [ + 'react-native-windows/jest/setup', + './jest.perf.setup.ts', + ], + + testEnvironmentOptions: { + app: 'RNTesterApp-Fabric', + useRootSession: true, + rootLaunchApp: true, + enableAutomationChannel: true, + }, + + transform: { + '\\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp)$': assetTransform, + 'node_modules\\\\@?react-native\\\\.*': 'babel-jest', + '@react-native-windows\\\\tester\\\\.*': 'babel-jest', + '@react-native-windows\\\\perf-testing\\\\.*': 'babel-jest', + 'vnext\\\\.*': 'babel-jest', + '\\.[jt]sx?$': 'babel-jest', + }, + + transformIgnorePatterns: ['jest-runner', 'node_modules\\\\safe-buffer'], + + moduleFileExtensions: [ + 'js', + 'windows.js', + 'android.js', + 'mjs', + 'cjs', + 'jsx', + 'ts', + 'windows.ts', + 'tsx', + 'windows.tsx', + 'json', + 'node', + ], + + verbose: true, + + reporters: process.env.CI + ? [ + 'default', + [ + '@react-native-windows/perf-testing/lib-commonjs/ci/PerfJsonReporter', + {outputFile: '.native-perf-results/results.json'}, + ], + ] + : ['default'], +}; diff --git a/packages/e2e-test-app-fabric/package.json b/packages/e2e-test-app-fabric/package.json index 673ec0fa80f..e217c70fc97 100644 --- a/packages/e2e-test-app-fabric/package.json +++ b/packages/e2e-test-app-fabric/package.json @@ -19,7 +19,10 @@ "perf:ci": "jest --config jest.perf.config.js --ci --forceExit", "perf:ci:compare": "node ../../vnext/Scripts/perf/compare-results.js", "perf:ci:report": "node ../../vnext/Scripts/perf/post-pr-comment.js", - "perf:create": "node ../../vnext/Scripts/perf/create-perf-test.js" + "perf:create": "node ../../vnext/Scripts/perf/create-perf-test.js", + "perf:native": "jest --config jest.native-perf.config.js", + "perf:native:update": "jest --config jest.native-perf.config.js -u", + "perf:native:ci": "jest --config jest.native-perf.config.js --ci --forceExit" }, "dependencies": { "@react-native-windows/automation-channel": "0.0.0-canary.1035", diff --git a/packages/e2e-test-app-fabric/test/NativePerfHelpers.ts b/packages/e2e-test-app-fabric/test/NativePerfHelpers.ts new file mode 100644 index 00000000000..589d36b9163 --- /dev/null +++ b/packages/e2e-test-app-fabric/test/NativePerfHelpers.ts @@ -0,0 +1,112 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + */ + +import {app} from '@react-native-windows/automation'; +import {goToComponentExample} from './RNTesterNavigation'; +import { + mean, + median, + standardDeviation, +} from '@react-native-windows/perf-testing'; +import type {PerfMetrics} from '@react-native-windows/perf-testing'; + +export interface NativePerfOptions { + runs?: number; + warmupRuns?: number; +} + +const DEFAULT_RUNS = 15; +const DEFAULT_WARMUP = 2; + +export async function navigateToBenchmarkPage(): Promise { + const componentsTab = await app.findElementByTestID('components-tab'); + await componentsTab.waitForDisplayed({timeout: 60000}); + + await goToComponentExample('Native Perf Benchmark'); +} + +export async function measureNativePerf( + componentName: string, + options?: NativePerfOptions, +): Promise { + const runs = options?.runs ?? DEFAULT_RUNS; + const warmupRuns = options?.warmupRuns ?? DEFAULT_WARMUP; + const totalRuns = runs + warmupRuns; + + const componentInput = await app.findElementByTestID('perf-component-input'); + await componentInput.waitForDisplayed({timeout: 5000}); + + await app.waitUntil( + async () => { + await componentInput.setValue(componentName); + return (await componentInput.getText()) === componentName; + }, + { + interval: 500, + timeout: 5000, + timeoutMsg: `Failed to set component: ${componentName}`, + }, + ); + + const runsInput = await app.findElementByTestID('perf-runs-input'); + const runsStr = String(totalRuns); + await app.waitUntil( + async () => { + await runsInput.setValue(runsStr); + return (await runsInput.getText()) === runsStr; + }, + {interval: 500, timeout: 5000, timeoutMsg: 'Failed to set run count'}, + ); + + const runBtn = await app.findElementByTestID('perf-run-btn'); + await runBtn.waitForDisplayed({timeout: 5000}); + await runBtn.click(); + + const statusEl = await app.findElementByTestID('perf-status'); + await app.waitUntil( + async () => { + const text = await statusEl.getText(); + return text === 'done'; + }, + { + interval: 1000, + timeout: 180000, + timeoutMsg: `Benchmark timed out for ${componentName}`, + }, + ); + + const resultsEl = await app.findElementByTestID('perf-results'); + const rawJson = await resultsEl.getText(); + const parsed = JSON.parse(rawJson) as { + componentName: string; + runs: number; + durations: number[]; + }; + + const durations = parsed.durations.slice(warmupRuns); + + if (durations.length < runs) { + throw new Error( + `Expected ${runs} durations after discarding ${warmupRuns} warmup, got ${durations.length}`, + ); + } + + const meanDuration = mean(durations); + const medianDuration = median(durations); + const stdDev = standardDeviation(durations); + + return { + name: `${componentName} native mount`, + meanDuration, + medianDuration, + stdDev, + renderCount: 1, + runs: durations.length, + durations, + timestamp: new Date().toISOString(), + nativeTimings: {fullPipeline: medianDuration}, + }; +} diff --git a/packages/e2e-test-app-fabric/test/__native_perf__/core/View.native-perf-test.ts b/packages/e2e-test-app-fabric/test/__native_perf__/core/View.native-perf-test.ts new file mode 100644 index 00000000000..075642b1262 --- /dev/null +++ b/packages/e2e-test-app-fabric/test/__native_perf__/core/View.native-perf-test.ts @@ -0,0 +1,78 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + */ + +import {app} from '@react-native-windows/automation'; +import {ThresholdPresets} from '@react-native-windows/perf-testing'; +import { + navigateToBenchmarkPage, + measureNativePerf, +} from '../../NativePerfHelpers'; + +beforeAll(async () => { + await app.setWindowPosition(0, 0); + await app.setWindowSize(1000, 1250); + await navigateToBenchmarkPage(); +}); + +const NATIVE = ThresholdPresets.native; + +describe('Core Components — Native Render Pipeline', () => { + test('View native mount', async () => { + const perf = await measureNativePerf('View', {runs: 15, warmupRuns: 2}); + expect(perf).toMatchPerfSnapshot(NATIVE); + }); + + test('Text native mount', async () => { + const perf = await measureNativePerf('Text', {runs: 15, warmupRuns: 2}); + expect(perf).toMatchPerfSnapshot(NATIVE); + }); + + test('TextInput native mount', async () => { + const perf = await measureNativePerf('TextInput', { + runs: 15, + warmupRuns: 2, + }); + expect(perf).toMatchPerfSnapshot(NATIVE); + }); + + test('Image native mount', async () => { + const perf = await measureNativePerf('Image', {runs: 15, warmupRuns: 2}); + expect(perf).toMatchPerfSnapshot(NATIVE); + }); + + test('Switch native mount', async () => { + const perf = await measureNativePerf('Switch', {runs: 15, warmupRuns: 2}); + expect(perf).toMatchPerfSnapshot(NATIVE); + }); + + test('ActivityIndicator native mount', async () => { + const perf = await measureNativePerf('ActivityIndicator', { + runs: 15, + warmupRuns: 2, + }); + expect(perf).toMatchPerfSnapshot(NATIVE); + }); + + test('ScrollView native mount', async () => { + const perf = await measureNativePerf('ScrollView', { + runs: 10, + warmupRuns: 2, + }); + expect(perf).toMatchPerfSnapshot({...NATIVE, maxCV: 0.6}); + }); + + test('FlatList native mount', async () => { + const perf = await measureNativePerf('FlatList', { + runs: 10, + warmupRuns: 2, + }); + expect(perf).toMatchPerfSnapshot({ + ...NATIVE, + maxDurationIncrease: 20, + maxCV: 0.6, + }); + }); +}); diff --git a/packages/e2e-test-app-fabric/test/__native_perf__/core/__perf_snapshots__/View.native-perf-test.ts.perf-baseline.json b/packages/e2e-test-app-fabric/test/__native_perf__/core/__perf_snapshots__/View.native-perf-test.ts.perf-baseline.json new file mode 100644 index 00000000000..1fc84e6f59e --- /dev/null +++ b/packages/e2e-test-app-fabric/test/__native_perf__/core/__perf_snapshots__/View.native-perf-test.ts.perf-baseline.json @@ -0,0 +1,320 @@ +{ + "Core Components — Native Render Pipeline View native mount 1": { + "metrics": { + "name": "View native mount", + "meanDuration": 7.965346668163935, + "medianDuration": 7.814400002360344, + "stdDev": 0.7503939989497768, + "renderCount": 1, + "runs": 15, + "durations": [ + 9.190099999308586, + 7.984700009226799, + 7.7070000022649765, + 7.963699996471405, + 7.657600000500679, + 8.263199999928474, + 7.814400002360344, + 7.363900005817413, + 7.269499987363815, + 7.991300001740456, + 7.2847999930381775, + 7.353300005197525, + 7.56980000436306, + 8.03660000860691, + 10.030300006270409 + ], + "timestamp": "2026-03-12T07:50:20.925Z", + "nativeTimings": { + "fullPipeline": 7.814400002360344 + } + }, + "threshold": { + "maxDurationIncrease": 15, + "maxDuration": null, + "minAbsoluteDelta": 5, + "maxRenderCount": 1, + "minRuns": 10, + "maxCV": 0.5, + "mode": "gate" + }, + "capturedAt": "2026-03-12T07:50:20.926Z" + }, + "Core Components — Native Render Pipeline Text native mount 1": { + "metrics": { + "name": "Text native mount", + "meanDuration": 8.775593332449596, + "medianDuration": 8.77089999616146, + "stdDev": 0.4363329788038993, + "renderCount": 1, + "runs": 15, + "durations": [ + 8.25840000808239, + 9.665399998426437, + 8.581399992108345, + 8.77089999616146, + 9.273499995470047, + 9.550700008869171, + 8.49210000038147, + 8.254999995231628, + 8.475600004196167, + 8.775199994444847, + 8.822300001978874, + 8.951499998569489, + 8.40929999947548, + 8.890000000596046, + 8.462599992752075 + ], + "timestamp": "2026-03-12T07:50:23.671Z", + "nativeTimings": { + "fullPipeline": 8.77089999616146 + } + }, + "threshold": { + "maxDurationIncrease": 15, + "maxDuration": null, + "minAbsoluteDelta": 5, + "maxRenderCount": 1, + "minRuns": 10, + "maxCV": 0.5, + "mode": "gate" + }, + "capturedAt": "2026-03-12T07:50:23.673Z" + }, + "Core Components — Native Render Pipeline TextInput native mount 1": { + "metrics": { + "name": "TextInput native mount", + "meanDuration": 11.196973330775897, + "medianDuration": 11.007999986410141, + "stdDev": 1.0020838707600381, + "renderCount": 1, + "runs": 15, + "durations": [ + 10.245399996638298, + 11.007999986410141, + 11.696299999952316, + 10.852799996733665, + 11.64869999885559, + 11.229299992322922, + 13.262100011110306, + 12.7364000082016, + 10.52199999988079, + 10.773599997162819, + 11.078599989414215, + 10.190099999308586, + 10.107699990272522, + 10.069199994206429, + 12.534400001168251 + ], + "timestamp": "2026-03-12T07:50:26.745Z", + "nativeTimings": { + "fullPipeline": 11.007999986410141 + } + }, + "threshold": { + "maxDurationIncrease": 15, + "maxDuration": null, + "minAbsoluteDelta": 5, + "maxRenderCount": 1, + "minRuns": 10, + "maxCV": 0.5, + "mode": "gate" + }, + "capturedAt": "2026-03-12T07:50:26.749Z" + }, + "Core Components — Native Render Pipeline Image native mount 1": { + "metrics": { + "name": "Image native mount", + "meanDuration": 10.385873334606488, + "medianDuration": 9.600000008940697, + "stdDev": 1.9376943291015232, + "renderCount": 1, + "runs": 15, + "durations": [ + 13.439300000667572, + 10.480499997735023, + 9.600000008940697, + 10.026899993419647, + 10.172600001096725, + 9.45890000462532, + 9.476500004529953, + 9.03320001065731, + 8.662799999117851, + 9.664900004863739, + 8.988000005483627, + 8.744599997997284, + 9.567399993538857, + 13.752700001001358, + 14.719799995422363 + ], + "timestamp": "2026-03-12T07:50:29.540Z", + "nativeTimings": { + "fullPipeline": 9.600000008940697 + } + }, + "threshold": { + "maxDurationIncrease": 15, + "maxDuration": null, + "minAbsoluteDelta": 5, + "maxRenderCount": 1, + "minRuns": 10, + "maxCV": 0.5, + "mode": "gate" + }, + "capturedAt": "2026-03-12T07:50:29.542Z" + }, + "Core Components — Native Render Pipeline Switch native mount 1": { + "metrics": { + "name": "Switch native mount", + "meanDuration": 8.576380000511806, + "medianDuration": 8.229400008916855, + "stdDev": 0.9039821852849624, + "renderCount": 1, + "runs": 15, + "durations": [ + 8.542500004172325, + 7.743599995970726, + 8.209299996495247, + 8.209900006651878, + 8.184499993920326, + 8.811700001358986, + 8.229400008916855, + 10.605000004172325, + 7.775299996137619, + 9.013999998569489, + 8.496399998664856, + 10.643399998545647, + 8.300200000405312, + 8.071400001645088, + 7.8091000020504 + ], + "timestamp": "2026-03-12T07:50:32.416Z", + "nativeTimings": { + "fullPipeline": 8.229400008916855 + } + }, + "threshold": { + "maxDurationIncrease": 15, + "maxDuration": null, + "minAbsoluteDelta": 5, + "maxRenderCount": 1, + "minRuns": 10, + "maxCV": 0.5, + "mode": "gate" + }, + "capturedAt": "2026-03-12T07:50:32.418Z" + }, + "Core Components — Native Render Pipeline ActivityIndicator native mount 1": { + "metrics": { + "name": "ActivityIndicator native mount", + "meanDuration": 9.51423999965191, + "medianDuration": 9.150399997830391, + "stdDev": 1.159785279255848, + "renderCount": 1, + "runs": 15, + "durations": [ + 10.353200003504753, + 13.115099996328354, + 9.150399997830391, + 9.891000002622604, + 9.216000005602837, + 9.149099990725517, + 9.298500001430511, + 9.369599997997284, + 8.87389999628067, + 8.784400001168251, + 8.941699996590614, + 10.549700006842613, + 8.427299991250038, + 8.64869999885559, + 8.945000007748604 + ], + "timestamp": "2026-03-12T07:50:35.939Z", + "nativeTimings": { + "fullPipeline": 9.150399997830391 + } + }, + "threshold": { + "maxDurationIncrease": 15, + "maxDuration": null, + "minAbsoluteDelta": 5, + "maxRenderCount": 1, + "minRuns": 10, + "maxCV": 0.5, + "mode": "gate" + }, + "capturedAt": "2026-03-12T07:50:35.941Z" + }, + "Core Components — Native Render Pipeline ScrollView native mount 1": { + "metrics": { + "name": "ScrollView native mount", + "meanDuration": 31.650409997999667, + "medianDuration": 30.47995000332594, + "stdDev": 2.871197652832321, + "renderCount": 1, + "runs": 10, + "durations": [ + 39.441200003027916, + 31.205299988389015, + 33.09729999303818, + 30.64819999039173, + 30.543700009584427, + 30.41619999706745, + 30.345899999141693, + 30.254200011491776, + 30.3868999928236, + 30.165199995040894 + ], + "timestamp": "2026-03-12T07:50:39.028Z", + "nativeTimings": { + "fullPipeline": 30.47995000332594 + } + }, + "threshold": { + "maxDurationIncrease": 15, + "maxDuration": null, + "minAbsoluteDelta": 5, + "maxRenderCount": 1, + "minRuns": 10, + "maxCV": 0.6, + "mode": "gate" + }, + "capturedAt": "2026-03-12T07:50:39.030Z" + }, + "Core Components — Native Render Pipeline FlatList native mount 1": { + "metrics": { + "name": "FlatList native mount", + "meanDuration": 42.573099999129774, + "medianDuration": 42.00689999759197, + "stdDev": 1.5275286028989794, + "renderCount": 1, + "runs": 10, + "durations": [ + 45.16009999811649, + 42.16879999637604, + 41.925499990582466, + 41.383200004696846, + 44.674799993634224, + 42.08830000460148, + 41.30290000140667, + 41.59589999914169, + 44.304399996995926, + 41.12710000574589 + ], + "timestamp": "2026-03-12T07:50:42.007Z", + "nativeTimings": { + "fullPipeline": 42.00689999759197 + } + }, + "threshold": { + "maxDurationIncrease": 20, + "maxDuration": null, + "minAbsoluteDelta": 5, + "maxRenderCount": 1, + "minRuns": 10, + "maxCV": 0.6, + "mode": "gate" + }, + "capturedAt": "2026-03-12T07:50:42.010Z" + } +} From c17c4fb706bd0f95341c0ad1313bd42c4b229c7a Mon Sep 17 00:00:00 2001 From: Abhijeet Jha <74712637+iamAbhi-916@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:58:21 +0530 Subject: [PATCH 02/11] Added all 6 missing components --- .../NativePerfBenchmarkExample.js | 37 ++ .../core/View.native-perf-test.ts | 46 ++ ...iew.native-perf-test.ts.perf-baseline.json | 557 +++++++++++++----- 3 files changed, 482 insertions(+), 158 deletions(-) diff --git a/packages/@react-native-windows/tester/src/js/examples-win/NativePerfBenchmark/NativePerfBenchmarkExample.js b/packages/@react-native-windows/tester/src/js/examples-win/NativePerfBenchmark/NativePerfBenchmarkExample.js index dab42e7b432..d0f48ed7cc4 100644 --- a/packages/@react-native-windows/tester/src/js/examples-win/NativePerfBenchmark/NativePerfBenchmarkExample.js +++ b/packages/@react-native-windows/tester/src/js/examples-win/NativePerfBenchmark/NativePerfBenchmarkExample.js @@ -14,9 +14,14 @@ const { Image, ScrollView, FlatList, + SectionList, Switch, ActivityIndicator, + Button, + Modal, Pressable, + TouchableHighlight, + TouchableOpacity, StyleSheet, } = require('react-native'); @@ -53,8 +58,40 @@ const COMPONENT_REGISTRY = { renderItem={({item}) => {item.key}} /> ), + SectionList: () => ( + {item}} + renderSectionHeader={({section}) => {section.title}} + /> + ), Switch: () => , ActivityIndicator: () => , + Button: () =>