diff --git a/.github/workflows/perf-tests.yml b/.github/workflows/perf-tests.yml index d29a71a3290..762643cc172 100644 --- a/.github/workflows/perf-tests.yml +++ b/.github/workflows/perf-tests.yml @@ -27,7 +27,7 @@ jobs: perf-tests: name: Component Performance Tests runs-on: windows-latest - timeout-minutes: 30 + timeout-minutes: 60 permissions: contents: read @@ -49,9 +49,31 @@ jobs: - name: Install dependencies run: yarn install --frozen-lockfile + - name: Install Windows SDK 10.0.22621 + shell: pwsh + run: | + $installerUrl = "https://download.microsoft.com/download/3/b/d/3bd97f81-3f5b-4922-b86d-dc5145cd6bfe/windowssdk/winsdksetup.exe" + $installerPath = "$env:TEMP\winsdksetup.exe" + Invoke-WebRequest -Uri $installerUrl -OutFile $installerPath + Start-Process -FilePath $installerPath -ArgumentList '/quiet', '/norestart', '/features', '+' -Wait -NoNewWindow + $sdkPath = "${env:ProgramFiles(x86)}\Windows Kits\10\Include\10.0.22621.0" + if (!(Test-Path $sdkPath)) { + echo "::error::Failed to install Windows SDK 10.0.22621" + exit 1 + } + - name: Build perf-testing package run: yarn workspace @react-native-windows/perf-testing build + - name: Enable Developer Mode + shell: pwsh + run: reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v AllowDevelopmentWithoutDevLicense /d 1 + + # ── Build & Deploy RNTesterApp-Fabric (for native perf tests) ── + - name: Build and deploy RNTesterApp-Fabric + working-directory: packages/e2e-test-app-fabric + run: yarn windows --release --no-launch --logging + # ── Run Tests ────────────────────────────────────────── - name: Run perf tests id: perf-run @@ -61,7 +83,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 +109,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/change/@react-native-windows-automation-a4bcc22f-ba9f-409b-baeb-eac0e646b9d6.json b/change/@react-native-windows-automation-a4bcc22f-ba9f-409b-baeb-eac0e646b9d6.json new file mode 100644 index 00000000000..49d47680c40 --- /dev/null +++ b/change/@react-native-windows-automation-a4bcc22f-ba9f-409b-baeb-eac0e646b9d6.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "add native perf benchmarking infrastructure for Fabric components", + "packageName": "@react-native-windows/automation", + "email": "74712637+iamAbhi-916@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@react-native-windows-perf-testing-4734971c-72bb-4389-b990-27e212a15295.json b/change/@react-native-windows-perf-testing-4734971c-72bb-4389-b990-27e212a15295.json new file mode 100644 index 00000000000..0235cff197b --- /dev/null +++ b/change/@react-native-windows-perf-testing-4734971c-72bb-4389-b990-27e212a15295.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "add native perf benchmarking infrastructure for Fabric components", + "packageName": "@react-native-windows/perf-testing", + "email": "74712637+iamAbhi-916@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/@react-native-windows/automation/src/AutomationEnvironment.ts b/packages/@react-native-windows/automation/src/AutomationEnvironment.ts index d12b3d4da9d..9088328a9d6 100644 --- a/packages/@react-native-windows/automation/src/AutomationEnvironment.ts +++ b/packages/@react-native-windows/automation/src/AutomationEnvironment.ts @@ -209,16 +209,24 @@ export default class AutomationEnvironment extends NodeEnvironment { // Set up the "Desktop" or Root session const rootBrowser = await webdriverio.remote(this.rootWebDriverOptions); - // Get the list of windows - const allWindows = await rootBrowser.$$('//Window'); - - // Find our target window + // Poll for the app window with timeout (cold starts can be slow) + const windowTimeout = 300000; // 5 minutes + const pollInterval = 2000; + const deadline = Date.now() + windowTimeout; let appWindow: webdriverio.Element | undefined; - for (const window of allWindows) { - if ((await window.getAttribute('Name')) === appName) { - appWindow = window; + + while (Date.now() < deadline) { + const allWindows = await rootBrowser.$$('//Window'); + for (const window of allWindows) { + if ((await window.getAttribute('Name')) === appName) { + appWindow = window; + break; + } + } + if (appWindow) { break; } + await new Promise(resolve => setTimeout(resolve, pollInterval)); } if (!appWindow) { 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..d0f48ed7cc4 --- /dev/null +++ b/packages/@react-native-windows/tester/src/js/examples-win/NativePerfBenchmark/NativePerfBenchmarkExample.js @@ -0,0 +1,265 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + */ + +'use strict'; + +const React = require('react'); +const { + View, + Text, + TextInput, + Image, + ScrollView, + FlatList, + SectionList, + Switch, + ActivityIndicator, + Button, + Modal, + Pressable, + TouchableHighlight, + TouchableOpacity, + 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}} + /> + ), + SectionList: () => ( + {item}} + renderSectionHeader={({section}) => {section.title}} + /> + ), + Switch: () => , + ActivityIndicator: () => , + Button: () =>