Refactor native/js modes (#52822)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52822

Refactors underlying modes by adding `isNativeOpt`, `isJsOpt`, and `isJsBytecode` to allow for more granular control in a future diff.

### View ###

| (index) | Task name                                                 | Latency average (ns)   | Latency median (ns)       | Throughput average (ops/s) | Throughput median (ops/s) | Samples |
| ------- | --------------------------------------------------------- | ---------------------- | ------------------------- | -------------------------- | ------------------------- | ------- |
| 0       | 'render 100 uncollapsable views'                          | '23005778.16 ± 0.53%'  | '22877194.50 ± 11607.50'  | '43 ± 0.50%'               | '44'                      | 64      |
| 1       | 'render 1000 uncollapsable views'                         | '271276451.70 ± 0.61%' | '268378201.00 ± 9925.00'  | '4 ± 0.59%'                | '4'                       | 64      |
| 2       | 'render 100 views with large amount of props and styles'  | '47580650.91 ± 1.21%'  | '47212012.00 ± 2979.00'   | '21 ± 0.89%'               | '21'                      | 64      |
| 3       | 'render 1000 views with large amount of props and styles' | '521237370.22 ± 1.09%' | '516142815.00 ± 41682.00' | '2 ± 0.84%'                | '2'                       | 64      |
| 4       | 'render 1500 views with large amount of props and styles' | '828143691.48 ± 0.94%' | '824723257.50 ± 11331.50' | '1 ± 0.73%'                | '1'                       | 64      |

### View (mode 🚀, jsMode 🚀, bytecode) ###

| (index) | Task name                                                 | Latency average (ns)   | Latency median (ns)        | Throughput average (ops/s) | Throughput median (ops/s) | Samples |
| ------- | --------------------------------------------------------- | ---------------------- | -------------------------- | -------------------------- | ------------------------- | ------- |
| 0       | 'render 100 uncollapsable views'                          | '4051033.45 ± 2.01%'   | '3876618.00'               | '251 ± 1.29%'              | '258'                     | 247     |
| 1       | 'render 1000 uncollapsable views'                         | '86134420.23 ± 1.38%'  | '85815369.50 ± 281477.50'  | '12 ± 1.38%'               | '12'                      | 64      |
| 2       | 'render 100 views with large amount of props and styles'  | '13921817.92 ± 2.57%'  | '13474963.50 ± 4977.50'    | '72 ± 1.62%'               | '74'                      | 72      |
| 3       | 'render 1000 views with large amount of props and styles' | '182664526.31 ± 0.74%' | '181872565.00 ± 10281.00'  | '5 ± 0.73%'                | '5'                       | 64      |
| 4       | 'render 1500 views with large amount of props and styles' | '313110386.45 ± 1.13%' | '307934163.50 ± 156920.50' | '3 ± 1.07%'                | '3'                       | 64      |

Changelog: [Internal]

Reviewed By: rubennorte

Differential Revision: D78912257

fbshipit-source-id: 16fd0301af98159dbb9818cb8092bd4416ef2559
This commit is contained in:
Devan Buggay
2025-07-30 14:22:22 -07:00
committed by Facebook GitHub Bot
parent 807f0b6882
commit 6d51bce9ed
5 changed files with 94 additions and 89 deletions
+6 -10
View File
@@ -132,8 +132,6 @@ Available pragmas:
- Possible values:
- `dev`: development, default for tests.
- `opt`: optimized and using Hermes bytecode, default for benchmarks.
- `dev-bytecode`: development but using Hermes bytecode instead of plain
text JavaScript code.
- `@fantom_react_fb_flags`: used to set overrides for internal React flags set
in ReactNativeInternalFeatureFlags (Meta use only)
@@ -150,14 +148,12 @@ this test:
Would be executed with these combinations of options:
| `jsOnlyTestFlag` | `mode` |
| ---------------- | -------------- |
| `false` | `dev` |
| `true` | `dev` |
| `false` | `dev-bytecode` |
| `true` | `dev-bytecode` |
| `false` | `opt` |
| `true` | `opt` |
| `jsOnlyTestFlag` | `mode` |
| ---------------- | ------ |
| `false` | `dev` |
| `true` | `dev` |
| `false` | `opt` |
| `true` | `opt` |
With an output such as:
+32 -15
View File
@@ -11,22 +11,41 @@
import type {FeatureFlagValue} from '../../../packages/react-native/scripts/featureflags/types';
import type {FantomTestConfig} from '../runner/getFantomTestConfigs';
import type {HermesVariant} from '../runner/utils';
import type {PartialFantomTestConfig} from './getFantomTestConfigs';
import {
FantomTestConfigHermesVariant,
FantomTestConfigMode,
} from '../runner/getFantomTestConfigs';
import {FantomTestConfigHermesVariant} from '../runner/getFantomTestConfigs';
import {getOverrides} from './getFantomTestConfigs';
function formatFantomMode(mode: FantomTestConfigMode): string {
switch (mode) {
case FantomTestConfigMode.DevelopmentWithSource:
return 'mode 🐛';
case FantomTestConfigMode.DevelopmentWithBytecode:
return 'mode 🐛🔢';
case FantomTestConfigMode.Optimized:
return 'mode 🚀';
function formatModes(overrides: PartialFantomTestConfig) {
const parts = [];
if (
overrides.isNativeOptimized === false &&
overrides.isJsOptimized === false &&
overrides.isJsBytecode === false
) {
return ['mode 🐛'];
} else if (
overrides.isNativeOptimized === true &&
overrides.isJsOptimized === true &&
overrides.isJsBytecode === true
) {
return ['mode 🚀'];
}
if (overrides.isNativeOptimized != null) {
parts.push(overrides.isNativeOptimized ? 'native 🚀' : 'native 🐛');
}
if (overrides.isJsOptimized != null) {
parts.push(overrides.isJsOptimized ? 'js 🚀' : 'js 🐛');
}
if (overrides.isJsBytecode != null && overrides.isJsBytecode) {
parts.push('bytecode');
}
return parts;
}
function formatFantomHermesVariant(hermesVariant: HermesVariant): string {
@@ -55,9 +74,7 @@ export default function formatFantomConfig(config: FantomTestConfig): string {
const overrides = getOverrides(config);
const parts = [];
if (overrides.mode) {
parts.push(formatFantomMode(overrides.mode));
}
parts.push(...formatModes(overrides));
if (overrides.hermesVariant) {
parts.push(formatFantomHermesVariant(overrides.hermesVariant));
+47 -28
View File
@@ -20,12 +20,6 @@ type JsOnlyFeatureFlags = (typeof ReactNativeFeatureFlags)['jsOnly'];
type DocblockPragmas = {[key: string]: string | string[]};
export enum FantomTestConfigMode {
DevelopmentWithBytecode,
DevelopmentWithSource,
Optimized,
}
export type FantomTestConfigCommonFeatureFlags = Partial<{
[key in keyof CommonFeatureFlags]: CommonFeatureFlags[key]['defaultValue'],
}>;
@@ -45,22 +39,26 @@ export type FantomTestConfigFeatureFlags = {
};
export type FantomTestConfig = {
mode: FantomTestConfigMode,
isNativeOptimized: boolean,
isJsOptimized: boolean,
isJsBytecode: boolean,
hermesVariant: HermesVariant,
flags: FantomTestConfigFeatureFlags,
};
export type PartialFantomTestConfig = {
mode?: FantomTestConfigMode,
isNativeOptimized?: boolean,
isJsOptimized?: boolean,
isJsBytecode?: boolean,
hermesVariant?: HermesVariant,
flags?: Partial<FantomTestConfigFeatureFlags>,
};
export const FantomTestConfigHermesVariant = HermesVariant;
export const DEFAULT_MODE: FantomTestConfigMode =
FantomTestConfigMode.DevelopmentWithSource;
export const DEFAULT_IS_NATIVE_OPTIMIZED: boolean = false;
export const DEFAULT_IS_JS_OPTIMIZED: boolean = false;
export const DEFAULT_IS_JS_BYTECODE: boolean = false;
export const DEFAULT_HERMES_VARIANT: HermesVariant = HermesVariant.Hermes;
export const DEFAULT_FEATURE_FLAGS: FantomTestConfigFeatureFlags = {
@@ -77,9 +75,6 @@ const FANTOM_BENCHMARK_FILENAME_RE = /[Bb]enchmark-itest\./g;
const FANTOM_BENCHMARK_SUITE_RE =
/\n(Fantom\.)?unstable_benchmark(\s*)\.suite\(/g;
const FANTOM_BENCHMARK_DEFAULT_MODE: FantomTestConfigMode =
FantomTestConfigMode.Optimized;
const MAX_FANTOM_CONFIGURATION_VARIATIONS = 12;
const VALID_FANTOM_PRAGMAS = [
@@ -94,8 +89,16 @@ export function getOverrides(
): PartialFantomTestConfig {
const overrides: PartialFantomTestConfig = {};
if (config.mode !== DEFAULT_MODE) {
overrides.mode = config.mode;
if (config.isNativeOptimized !== DEFAULT_IS_NATIVE_OPTIMIZED) {
overrides.isNativeOptimized = config.isNativeOptimized;
}
if (config.isJsOptimized !== DEFAULT_IS_JS_OPTIMIZED) {
overrides.isJsOptimized = config.isJsOptimized;
}
if (config.isJsBytecode !== DEFAULT_IS_JS_BYTECODE) {
overrides.isJsBytecode = config.isJsBytecode;
}
if (config.hermesVariant !== DEFAULT_HERMES_VARIANT) {
@@ -140,7 +143,7 @@ export function getOverrides(
*
* The supported options are:
* - `fantom_mode`: specifies the level of optimization to compile the test
* with. Valid values are `dev`, `dev-bytecode` and `opt`.
* with. Valid values are `dev` and `opt`.
* - `fantom_hermes_variant`: specifies the Hermes variant to use to run the
* test. Valid values are `hermes`, `static_hermes_stable` and
* `static_hermes_experimental`.
@@ -162,7 +165,9 @@ export default function getFantomTestConfigs(
verifyFantomPragmas(pragmas);
const config: FantomTestConfig = {
mode: DEFAULT_MODE,
isNativeOptimized: DEFAULT_IS_NATIVE_OPTIMIZED,
isJsOptimized: DEFAULT_IS_JS_OPTIMIZED,
isJsBytecode: DEFAULT_IS_JS_BYTECODE,
hermesVariant: DEFAULT_HERMES_VARIANT,
flags: {
common: {
@@ -190,19 +195,27 @@ export default function getFantomTestConfigs(
switch (mode) {
case 'dev':
config.mode = FantomTestConfigMode.DevelopmentWithSource;
break;
case 'dev-bytecode':
config.mode = FantomTestConfigMode.DevelopmentWithBytecode;
config.isNativeOptimized = false;
config.isJsOptimized = false;
config.isJsBytecode = false;
break;
case 'opt':
config.mode = FantomTestConfigMode.Optimized;
config.isNativeOptimized = true;
config.isJsOptimized = true;
config.isJsBytecode = true;
break;
case '*':
configVariations.push([
{mode: FantomTestConfigMode.DevelopmentWithSource},
{mode: FantomTestConfigMode.DevelopmentWithBytecode},
{mode: FantomTestConfigMode.Optimized},
{
isNativeOptimized: false,
isJsOptimized: false,
isJsBytecode: false,
},
{
isNativeOptimized: true,
isJsOptimized: true,
isJsBytecode: true,
},
]);
break;
default:
@@ -213,7 +226,9 @@ export default function getFantomTestConfigs(
FANTOM_BENCHMARK_FILENAME_RE.test(testPath) ||
FANTOM_BENCHMARK_SUITE_RE.test(testContents)
) {
config.mode = FANTOM_BENCHMARK_DEFAULT_MODE;
config.isNativeOptimized = true;
config.isJsOptimized = true;
config.isJsBytecode = true;
}
}
@@ -373,7 +388,11 @@ function getConfigurationVariations(
for (const currentConfigVariation of currentConfigVariations) {
const currentConfigWithVariation = {
mode: currentConfigVariation.mode ?? config.mode,
isNativeOptimized:
currentConfigVariation.isNativeOptimized ?? config.isNativeOptimized,
isJsOptimized:
currentConfigVariation.isJsOptimized ?? config.isJsOptimized,
isJsBytecode: currentConfigVariation.isJsBytecode ?? config.isJsBytecode,
hermesVariant:
currentConfigVariation.hermesVariant ?? config.hermesVariant,
flags: {
+9 -20
View File
@@ -24,7 +24,6 @@ import entrypointTemplate from './entrypoint-template';
import * as EnvironmentOptions from './EnvironmentOptions';
import formatFantomConfig from './formatFantomConfig';
import getFantomTestConfigs from './getFantomTestConfigs';
import {FantomTestConfigMode} from './getFantomTestConfigs';
import {
getInitialSnapshotData,
updateSnapshotsAndGetJestSnapshotResult,
@@ -249,10 +248,7 @@ module.exports = async function runTest(
];
for (const testConfig of testConfigs) {
if (
EnvironmentOptions.isOSS &&
testConfig.mode === FantomTestConfigMode.Optimized
) {
if (EnvironmentOptions.isOSS && testConfig.isNativeOptimized) {
testResultsByConfig.push(
skippedTestResults({
ancestorTitles: ['"@fantom_mode opt" in docblock'],
@@ -277,13 +273,10 @@ module.exports = async function runTest(
continue;
}
if (
EnvironmentOptions.isOSS &&
testConfig.mode !== FantomTestConfigMode.DevelopmentWithSource
) {
if (EnvironmentOptions.isOSS && testConfig.isJsBytecode) {
testResultsByConfig.push(
skippedTestResults({
ancestorTitles: ['"@fantom_mode dev-bytecode" in docblock'],
ancestorTitles: ['"@fantom_mode dev" in docblock'],
title: 'Hermes bytecode is not yet supported in OSS',
}),
);
@@ -320,26 +313,24 @@ module.exports = async function runTest(
entry: entrypointPath,
out: testJSBundlePath,
platform: 'android',
minify: testConfig.mode === FantomTestConfigMode.Optimized,
dev: testConfig.mode !== FantomTestConfigMode.Optimized,
minify: testConfig.isJsOptimized,
dev: !testConfig.isJsOptimized,
sourceMap: true,
sourceMapUrl: sourceMapPath,
});
if (testConfig.mode !== FantomTestConfigMode.DevelopmentWithSource) {
if (testConfig.isJsBytecode) {
generateBytecodeBundle({
sourcePath: testJSBundlePath,
bytecodePath: testBytecodeBundlePath,
isOptimizedMode: testConfig.mode === FantomTestConfigMode.Optimized,
isOptimizedMode: testConfig.isJsOptimized,
hermesVariant: testConfig.hermesVariant,
});
}
const rnTesterCommandArgs = [
'--bundlePath',
testConfig.mode === FantomTestConfigMode.DevelopmentWithSource
? testJSBundlePath
: testBytecodeBundlePath,
!testConfig.isJsBytecode ? testJSBundlePath : testBytecodeBundlePath,
'--featureFlags',
JSON.stringify(testConfig.flags.common),
'--minLogLevel',
@@ -354,9 +345,7 @@ module.exports = async function runTest(
: runBuck2(
[
'run',
...getBuckModesForPlatform(
testConfig.mode === FantomTestConfigMode.Optimized,
),
...getBuckModesForPlatform(testConfig.isNativeOptimized),
...getBuckOptionsForHermes(testConfig.hermesVariant),
'//xplat/js/react-native-github/private/react-native-fantom/tester:tester',
'--',
@@ -1,16 +0,0 @@
/**
* 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.
*
* @fantom_mode dev-bytecode
* @flow strict-local
* @format
*/
describe('"@fantom_mode dev-bytecode" in docblock', () => {
it('should use development builds', () => {
expect(__DEV__).toBe(true);
});
});