forked from facebook/react-native
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[skip ci] Add API to run benchmarks in Fantom (facebook#48452)
Summary: Changelog: [internal] Implements a basic API to run benchmarks with Fantom (using `tinybench` under the hood): ``` import {benchmark} from 'react-native/fantom'; benchmark .suite('Suite name', { // options }) .add( 'Test name', () => { // code to benchmark }, { beforeAll: () => {}, beforeEach: () => {}, afterEach: () => {}, afterAll: () => {}, }, ) .verify(results => { // check results and throw an error if the expectations fail }); ``` Features: * Print benchmark results in the console as a table. * It opts into optimized builds automatically * Verifies that optimized build is used (unless manually opting out of the check via `disableOptimizedBuildCheck`). * Supports verification of results (making expectations and making the test fail if the benchmark doesn't meet some expectations). Reviewed By: rshest Differential Revision: D66926183
- Loading branch information
1 parent
d05443c
commit 2280ae9
Showing
3 changed files
with
176 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/** | ||
* 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 nullthrows from 'nullthrows'; | ||
import NativeCPUTime from 'react-native/src/private/specs/modules/NativeCPUTime'; | ||
import { | ||
Bench, | ||
type BenchOptions, | ||
type FnOptions, | ||
type TaskResult, | ||
} from 'tinybench'; | ||
|
||
type SuiteOptions = $ReadOnly<{ | ||
...Pick< | ||
BenchOptions, | ||
'iterations' | 'time' | 'warmup' | 'warmupIterations' | 'warmupTime', | ||
>, | ||
disableOptimizedBuildCheck?: boolean, | ||
}>; | ||
|
||
type SuiteResults = Array<$ReadOnly<TaskResult>>; | ||
|
||
interface SuiteAPI { | ||
add(name: string, fn: () => void, options?: FnOptions): SuiteAPI; | ||
verify(fn: (results: SuiteResults) => void): SuiteAPI; | ||
} | ||
|
||
export function suite( | ||
suiteName: string, | ||
suiteOptions?: ?SuiteOptions, | ||
): SuiteAPI { | ||
const {disableOptimizedBuildCheck, ...benchOptions} = suiteOptions ?? {}; | ||
|
||
const bench = new Bench({ | ||
...benchOptions, | ||
name: suiteName, | ||
throws: true, | ||
now: () => NativeCPUTime.getCPUTimeNanos() / 1000000, | ||
}); | ||
|
||
const verifyFns = []; | ||
|
||
global.it(suiteName, () => { | ||
if (bench.tasks.length === 0) { | ||
throw new Error('No benchmark tests defined'); | ||
} | ||
|
||
bench.runSync(); | ||
|
||
printBenchmarkResults(bench); | ||
|
||
for (const verify of verifyFns) { | ||
verify(bench.results); | ||
} | ||
|
||
if (!NativeCPUTime.hasAccurateCPUTimeNanosForBenchmarks()) { | ||
throw new Error( | ||
'`NativeCPUTime` module does not provide accurate CPU time information in this environment. Please run the benchmarks in an environment where it does.', | ||
); | ||
} | ||
|
||
if (__DEV__ && disableOptimizedBuildCheck !== true) { | ||
throw new Error('Benchmarks should not be run in development mode'); | ||
} | ||
}); | ||
|
||
const suiteAPI = { | ||
add(name: string, fn: () => void, options?: FnOptions): SuiteAPI { | ||
bench.add(name, fn, options); | ||
return suiteAPI; | ||
}, | ||
verify(fn: (results: SuiteResults) => void): SuiteAPI { | ||
verifyFns.push(fn); | ||
return suiteAPI; | ||
}, | ||
}; | ||
|
||
return suiteAPI; | ||
} | ||
|
||
function printBenchmarkResults(bench: Bench) { | ||
const longestTaskNameLength = bench.tasks.reduce( | ||
(maxLength, task) => Math.max(maxLength, task.name.length), | ||
0, | ||
); | ||
const separatorWidth = 121 + longestTaskNameLength - 'Task name'.length; | ||
|
||
console.log('-'.repeat(separatorWidth)); | ||
console.log(bench.name); | ||
console.table(nullthrows(bench.table())); | ||
console.log('-'.repeat(separatorWidth) + '\n'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters