diff --git a/flow-typed/npm/tinybench_v3.1.x.js b/flow-typed/npm/tinybench_v3.1.x.js new file mode 100644 index 000000000000..ce884b5b94dd --- /dev/null +++ b/flow-typed/npm/tinybench_v3.1.x.js @@ -0,0 +1,121 @@ +/** + * 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 + * @format + * @oncall react_native + */ + +declare module 'tinybench' { + declare export class Task extends EventTarget { + name: string; + result: void | $ReadOnly; + runs: number; + + reset(): void; + run(): Promise; + runSync(): Task; + warmup(): Promise; + } + + export type Hook = ( + task: Task, + mode: 'run' | 'warmup', + ) => Promise | void; + + export type BenchOptions = { + iterations?: number, + name?: string, + now?: () => number, + setup?: Hook, + signal?: AbortSignal, + teardown?: Hook, + throws?: boolean, + time?: number, + warmup?: boolean, + warmupIterations?: number, + warmupTime?: number, + }; + + export interface Statistics { + aad: void | number; + critical: number; + df: number; + mad: void | number; + max: number; + mean: number; + min: number; + moe: number; + p50: void | number; + p75: void | number; + p99: void | number; + p995: void | number; + p999: void | number; + rme: number; + samples: number[]; + sd: number; + sem: number; + variance: number; + } + + export interface TaskResult { + critical: number; + df: number; + error?: Error; + hz: number; + latency: Statistics; + max: number; + mean: number; + min: number; + moe: number; + p75: number; + p99: number; + p995: number; + p999: number; + period: number; + rme: number; + samples: number[]; + sd: number; + sem: number; + throughput: Statistics; + totalTime: number; + variance: number; + } + + export type FnOptions = { + afterAll?: (this: Task) => void | Promise, + afterEach?: (this: Task) => void | Promise, + beforeAll?: (this: Task) => void | Promise, + beforeEach?: (this: Task) => void | Promise, + }; + + export type Fn = () => Promise | mixed; + + declare export class Bench extends EventTarget { + concurrency: null | 'task' | 'bench'; + name?: string; + opts: $ReadOnly; + threshold: number; + + constructor(options?: BenchOptions): this; + + // $FlowExpectedError[unsafe-getters-setters] + get results(): Array<$ReadOnly>; + + // $FlowExpectedError[unsafe-getters-setters] + get tasks(): Array; + + add(name: string, fn: Fn, fnOpts?: FnOptions): this; + getTask(name: string): void | Task; + remove(name: string): this; + reset(): void; + run(): Promise>; + runSync(): Array; + table( + convert?: (task: Task) => Record, + ): void | Array>; + } +} diff --git a/package.json b/package.json index 45b397ff3c0c..9f96dc46e282 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "shelljs": "^0.8.5", "signedsource": "^1.0.0", "supports-color": "^7.1.0", + "tinybench": "^3.1.0", "typescript": "5.0.4", "ws": "^6.2.3" }, diff --git a/packages/react-native/ReactCommon/react/nativemodule/cputime/CPUTime.h b/packages/react-native/ReactCommon/react/nativemodule/cputime/CPUTime.h new file mode 100644 index 000000000000..fe260ff03f21 --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/cputime/CPUTime.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#pragma once + +#ifdef USE_POSIX_TIME +#include +#else +#include +#endif + +#ifdef USE_POSIX_TIME + +namespace { +const double NANOSECONDS_IN_A_SECOND = 1000000000; +} // namespace + +#endif + +namespace facebook::react { + +#ifdef USE_POSIX_TIME + +inline double getCPUTimeNanos() { + struct timespec time {}; + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time); + return static_cast(time.tv_sec) * NANOSECONDS_IN_A_SECOND + + static_cast(time.tv_nsec); +} + +inline bool hasAccurateCPUTimeNanosForBenchmarks() { + return true; +} + +#else + +inline double getCPUTimeNanos() { + auto now = std::chrono::steady_clock::now(); + return static_cast( + std::chrono::duration_cast( + now.time_since_epoch()) + .count()); +} + +inline bool hasAccurateCPUTimeNanosForBenchmarks() { + return false; +} + +#endif + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/cputime/NativeCPUTime.cpp b/packages/react-native/ReactCommon/react/nativemodule/cputime/NativeCPUTime.cpp new file mode 100644 index 000000000000..498a21d09889 --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/cputime/NativeCPUTime.cpp @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#include "NativeCPUTime.h" + +#include "CPUTime.h" + +#ifdef RN_DISABLE_OSS_PLUGIN_HEADER +#include "Plugins.h" +#endif + +std::shared_ptr NativeCPUTimeModuleProvider( + std::shared_ptr jsInvoker) { + return std::make_shared(std::move(jsInvoker)); +} + +namespace facebook::react { + +NativeCPUTime::NativeCPUTime(std::shared_ptr jsInvoker) + : NativeCPUTimeCxxSpec(std::move(jsInvoker)) {} + +double NativeCPUTime::getCPUTimeNanos(jsi::Runtime& /*runtime*/) { + return facebook::react::getCPUTimeNanos(); +} + +bool NativeCPUTime::hasAccurateCPUTimeNanosForBenchmarks( + jsi::Runtime& /*runtime*/) { + return facebook::react::hasAccurateCPUTimeNanosForBenchmarks(); +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/cputime/NativeCPUTime.h b/packages/react-native/ReactCommon/react/nativemodule/cputime/NativeCPUTime.h new file mode 100644 index 000000000000..c6e60b4f6662 --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/cputime/NativeCPUTime.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#pragma once + +#if __has_include("rncoreJSI.h") // Cmake headers on Android +#include "rncoreJSI.h" +#elif __has_include("FBReactNativeSpecJSI.h") // CocoaPod headers on Apple +#include "FBReactNativeSpecJSI.h" +#else +#include +#endif + +namespace facebook::react { + +class NativeCPUTime : public NativeCPUTimeCxxSpec { + public: + explicit NativeCPUTime(std::shared_ptr jsInvoker); + + double getCPUTimeNanos(jsi::Runtime& runtime); + bool hasAccurateCPUTimeNanosForBenchmarks(jsi::Runtime& runtime); +}; + +} // namespace facebook::react diff --git a/packages/react-native/src/private/specs/modules/NativeCPUTime.js b/packages/react-native/src/private/specs/modules/NativeCPUTime.js new file mode 100644 index 000000000000..a2736dc86a08 --- /dev/null +++ b/packages/react-native/src/private/specs/modules/NativeCPUTime.js @@ -0,0 +1,24 @@ +/** + * 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 + * @format + */ + +import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport'; + +import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry'; + +/** + * This is an internal native module meant to be used for performance + * measurements and benchmarks. It is not meant to be used in production. + */ +export interface Spec extends TurboModule { + +getCPUTimeNanos: () => number; + +hasAccurateCPUTimeNanosForBenchmarks: () => boolean; +} + +export default (TurboModuleRegistry.getEnforcing('CPUTimeCxx'): Spec); diff --git a/yarn.lock b/yarn.lock index 0fc7c24ecfa8..307ba0037c45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8348,6 +8348,11 @@ tiny-invariant@^1.3.3: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== +tinybench@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-3.1.0.tgz#ec68451ff05233cf3de12c46f39f06011897109a" + integrity sha512-Km+oMh2xqNCxuyoUsqbRmHgFSd8sATh7v7xreP+kHN6x67w28Pawr83WmBxcaORvxkc0Ex6zgqK951yBnTFaaQ== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"