From 823adaa82faff4fb01d298429b91575a75311699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 18 Aug 2025 07:47:47 -0700 Subject: [PATCH 1/3] Print information about benchmark duration (#53323) Summary: Changelog: [internal] This just makes it easier to understand when the benchmark itself has started running and how long the benchmark itself took to run. Reviewed By: rshest Differential Revision: D80400268 --- private/react-native-fantom/src/Benchmark.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/private/react-native-fantom/src/Benchmark.js b/private/react-native-fantom/src/Benchmark.js index f5a78dca345c..d41039646688 100644 --- a/private/react-native-fantom/src/Benchmark.js +++ b/private/react-native-fantom/src/Benchmark.js @@ -174,10 +174,16 @@ export function suite( bench.add(task.name, task.fn, options); } + if (!isTestOnly) { + console.log(`Running benchmark: ${suiteName}. Please wait.`); + } + + const runStartTime = performance.now(); + bench.runSync(); if (!isTestOnly) { - printBenchmarkResults(bench); + printBenchmarkResults(bench, runStartTime); } for (const verify of verifyFns) { @@ -262,16 +268,24 @@ export function suite( return suiteAPI; } -function printBenchmarkResults(bench: Bench) { +function printBenchmarkResults(bench: Bench, runStartTime: number) { const {fantomConfigSummary} = getConstants(); const benchmarkName = (bench.name ?? 'Benchmark') + (fantomConfigSummary ? ` (${fantomConfigSummary})` : ''); + const runDuration = performance.now() - runStartTime; + const durationStr = + runDuration < 1000 + ? `${runDuration.toFixed(0)}ms` + : `${(runDuration / 1000).toFixed(0)}s`; + console.log(''); console.log(`### ${benchmarkName} ###`); console.table(nullthrows(bench.table())); console.log(''); + console.log(`Total benchmark duration: ${durationStr}`); + console.log(''); } function createBenchmarkResultsObject( From 1d6032b5924993216530ea82b7e73b0fbf9ccedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 18 Aug 2025 07:47:47 -0700 Subject: [PATCH 2/3] Improve typing of benchmark functions (#53322) Summary: Changelog: [internal] This changes the types for benchmark functions to improve safety: 1. It makes the return object be an object instead of an interface, to catch when `overriddenDuration` is misspelled. 2. It makes the function always synchronous, as asynchronous tests aren't supported in Fantom (even though they are in `tinybench`). Reviewed By: rshest Differential Revision: D80404121 --- flow-typed/npm/tinybench_v4.1.x.js | 11 +++++++--- private/react-native-fantom/src/Benchmark.js | 22 +++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/flow-typed/npm/tinybench_v4.1.x.js b/flow-typed/npm/tinybench_v4.1.x.js index a40214d96733..56412034a9d6 100644 --- a/flow-typed/npm/tinybench_v4.1.x.js +++ b/flow-typed/npm/tinybench_v4.1.x.js @@ -91,10 +91,15 @@ declare module 'tinybench' { beforeEach?: (this: Task) => void | Promise, }; - export interface FnReturnedObject { - overriddenDuration?: number; - } + // This is defined as an interface in tinybench but we define it as an object + // to catch problems like `overriddenDuration` being misspelled. + export type FnReturnedObject = { + overriddenDuration?: number, + }; + // This type is defined as returning `unknown` instead of `void` in tinybench, + // but we type it this way to avoid mistakes (we can make breaking changes + // in our definition that they can't). export type Fn = () => | Promise | void diff --git a/private/react-native-fantom/src/Benchmark.js b/private/react-native-fantom/src/Benchmark.js index d41039646688..2871312ef1aa 100644 --- a/private/react-native-fantom/src/Benchmark.js +++ b/private/react-native-fantom/src/Benchmark.js @@ -15,11 +15,13 @@ import NativeCPUTime from 'react-native/src/private/testing/fantom/specs/NativeC import { Bench, type BenchOptions, - type Fn, type FnOptions, + type FnReturnedObject, type TaskResult, } from 'tinybench'; +type SyncFn = () => FnReturnedObject | void; + export type SuiteOptions = $ReadOnly<{ minIterations?: number, minDuration?: number, @@ -66,20 +68,20 @@ interface ParameterizedTestFunction { ( testArgs: $ReadOnlyArray, name: TestWithArgName, - fn: (testArg: TestArgType) => ReturnType, + fn: (testArg: TestArgType) => ReturnType, options?: TestWithArgOptions, ): SuiteAPI; only: ( testArgs: $ReadOnlyArray, name: TestWithArgName, - fn: (testArg: TestArgType) => ReturnType, + fn: (testArg: TestArgType) => ReturnType, options?: TestWithArgOptions, ) => SuiteAPI; } interface TestFunction { - (name: string, fn: Fn, options?: FnOptions): SuiteAPI; - only: (name: string, fn: Fn, options?: FnOptions) => SuiteAPI; + (name: string, fn: SyncFn, options?: FnOptions): SuiteAPI; + only: (name: string, fn: SyncFn, options?: FnOptions) => SuiteAPI; // `each` allows to run the same test multiple times with different arguments, provided as an array of values: each: ParameterizedTestFunction; } @@ -91,7 +93,7 @@ interface SuiteAPI { interface TestTask { name: string; - fn: Fn; + fn: SyncFn; options: InternalTestOptions | void; } @@ -208,12 +210,12 @@ export function suite( reportBenchmarkResult(createBenchmarkResultsObject(bench, tasks)); }); - const test = (name: string, fn: Fn, options?: FnOptions): SuiteAPI => { + const test = (name: string, fn: SyncFn, options?: FnOptions): SuiteAPI => { tasks.push({name, fn, options}); return suiteAPI; }; - test.only = (name: string, fn: Fn, options?: FnOptions): SuiteAPI => { + test.only = (name: string, fn: SyncFn, options?: FnOptions): SuiteAPI => { tasks.push({name, fn, options: {...options, only: true}}); return suiteAPI; }; @@ -221,7 +223,7 @@ export function suite( const testWithArg = ( testArg: TestArgType, name: TestWithArgName, - fn: (testArg: TestArgType) => ReturnType, + fn: (testArg: TestArgType) => ReturnType, options?: TestWithArgOptions, only?: boolean = false, ): TestTask => { @@ -239,7 +241,7 @@ export function suite( const testEach: ParameterizedTestFunction = ( testArgs: $ReadOnlyArray, name: TestWithArgName, - fn: (testArg: TestArgType) => ReturnType, + fn: (testArg: TestArgType) => ReturnType, options?: TestWithArgOptions, ): SuiteAPI => { for (const testArg of testArgs) { From d1e96304733302b585f598ed4653d6af38ac4a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 18 Aug 2025 07:47:47 -0700 Subject: [PATCH 3/3] Rename minDuration as minTestExecutionTime for clarity (#53321) Summary: Changelog: [internal] Just a rename to make it easier to understand. Reviewed By: rshest Differential Revision: D80404378 --- .../__tests__/Performance-benchmark-itest.js | 2 +- private/react-native-fantom/src/Benchmark.js | 12 ++++++------ .../BenchmarkTests-testMode-benchmark-itest.js | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js b/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js index bf53c53fc11e..d69768d8d717 100644 --- a/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js +++ b/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js @@ -22,7 +22,7 @@ const clearMarksAndMeasures = () => { }; Fantom.unstable_benchmark - .suite('Performance API', {minIterations: 50000, minDuration: 0}) + .suite('Performance API', {minIterations: 50000}) .test( 'mark (default)', () => { diff --git a/private/react-native-fantom/src/Benchmark.js b/private/react-native-fantom/src/Benchmark.js index 2871312ef1aa..d1898b8ab1dc 100644 --- a/private/react-native-fantom/src/Benchmark.js +++ b/private/react-native-fantom/src/Benchmark.js @@ -24,9 +24,9 @@ type SyncFn = () => FnReturnedObject | void; export type SuiteOptions = $ReadOnly<{ minIterations?: number, - minDuration?: number, + minTestExecutionTimeMs?: number, warmup?: boolean, - minWarmupDuration?: number, + minWarmupDurationMs?: number, minWarmupIterations?: number, disableOptimizedBuildCheck?: boolean, testOnly?: boolean, @@ -141,16 +141,16 @@ export function suite( benchOptions.iterations = suiteOptions.minIterations; } - if (suiteOptions.minDuration != null) { - benchOptions.time = suiteOptions.minDuration; + if (suiteOptions.minTestExecutionTimeMs != null) { + benchOptions.time = suiteOptions.minTestExecutionTimeMs; } if (suiteOptions.warmup != null) { benchOptions.warmup = suiteOptions.warmup; } - if (suiteOptions.minWarmupDuration != null) { - benchOptions.warmupTime = suiteOptions.minWarmupDuration; + if (suiteOptions.minWarmupDurationMs != null) { + benchOptions.warmupTime = suiteOptions.minWarmupDurationMs; } if (suiteOptions.minWarmupIterations != null) { diff --git a/private/react-native-fantom/src/__tests__/benchmarks/BenchmarkTests-testMode-benchmark-itest.js b/private/react-native-fantom/src/__tests__/benchmarks/BenchmarkTests-testMode-benchmark-itest.js index 65eb176583b9..366bbb4e7a0f 100644 --- a/private/react-native-fantom/src/__tests__/benchmarks/BenchmarkTests-testMode-benchmark-itest.js +++ b/private/react-native-fantom/src/__tests__/benchmarks/BenchmarkTests-testMode-benchmark-itest.js @@ -27,8 +27,8 @@ Fantom.unstable_benchmark warmup: true, minWarmupIterations: 10, minIterations: 10, - minDuration: 1000, - minWarmupDuration: 1000, + minTestExecutionTimeMs: 1000, + minWarmupDurationMs: 1000, }) .test('test', () => { runs++;