Skip to content

Commit 8ef39b4

Browse files
rshestfacebook-github-bot
authored andcommitted
Implement benchmark comparison of different feture flag configurations (#52925)
Summary: Pull Request resolved: #52925 # Changelog: [Internal] - This adds an extra "ranking" report when running Fantom benchmark vs different React Feature flag configurations. It can be very useful when implementing some particular optimization, to streamline the before/after comparison wit this optimization enabled/disabled. Reviewed By: andrewdacenko Differential Revision: D79269601 fbshipit-source-id: f29e761e313d6857e5b3ac65faf2a387a84be9df
1 parent 664f7c0 commit 8ef39b4

4 files changed

Lines changed: 129 additions & 1 deletion

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
type TestTaskTiming = {
12+
name: string,
13+
latency: {
14+
mean: number,
15+
min: number,
16+
max: number,
17+
p50: number,
18+
p75: number,
19+
p99: number,
20+
},
21+
};
22+
23+
export type BenchmarkTestArtifact = {
24+
type: string,
25+
timings: $ReadOnlyArray<TestTaskTiming>,
26+
};
27+
28+
export const printBenchmarkResultsRanking = (
29+
testResults: Array<{
30+
title: string,
31+
testArtifact: mixed,
32+
}>,
33+
) => {
34+
const numberOfTests = testResults.length;
35+
if (numberOfTests <= 1 || testResults[0].testArtifact === undefined) {
36+
// No test variations to compare between, or there are no benchmak results present, just don't print anything
37+
return;
38+
}
39+
40+
const testTaskTimings: {[string]: Array<[string, number]>} = {};
41+
for (let i = 0; i < numberOfTests; i++) {
42+
if (testResults[i] == null) {
43+
continue;
44+
}
45+
// $FlowExpectedError[incompatible-cast]
46+
const testArtifact = testResults[i].testArtifact as ?BenchmarkTestArtifact;
47+
const testVariationName = testResults[i].title;
48+
if (
49+
testArtifact == null ||
50+
testArtifact.type !== 'benchmark' ||
51+
testVariationName == null
52+
) {
53+
continue;
54+
}
55+
for (const taskTiming of testArtifact.timings) {
56+
const taskName = taskTiming.name;
57+
if (testTaskTimings[taskName] === undefined) {
58+
testTaskTimings[taskName] = [];
59+
}
60+
testTaskTimings[taskName].push([
61+
testVariationName,
62+
taskTiming.latency.p50,
63+
]);
64+
}
65+
}
66+
67+
// Sort each task's executions by latency
68+
for (const taskName in testTaskTimings) {
69+
testTaskTimings[taskName].sort((a, b) => a[1] - b[1]);
70+
}
71+
72+
// Print the rankings
73+
console.log('### Benchmark Results Ranking ###');
74+
for (const taskName in testTaskTimings) {
75+
console.log(`> ${taskName}:`);
76+
let lastTiming;
77+
for (const [i, [testVariationName, latency]] of testTaskTimings[
78+
taskName
79+
].entries()) {
80+
console.log(
81+
` ${i + 1}. ${testVariationName}: ${latency.toFixed(2)}ms ${getTimingDelta(lastTiming, latency)}`,
82+
);
83+
lastTiming = latency;
84+
}
85+
console.log('\n');
86+
}
87+
};
88+
89+
function getTimingDelta(lastTiming: ?number, currentTiming: ?number): string {
90+
if (lastTiming != null && currentTiming != null) {
91+
const deltaPercent = ((currentTiming - lastTiming) / lastTiming) * 100;
92+
return `(${deltaPercent.toFixed(2)}% ${deltaPercent > 0 ? 'slower' : 'faster'})`;
93+
} else {
94+
return '';
95+
}
96+
}

private/react-native-fantom/runner/runner.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import type {
1414
TestSuiteResult,
1515
} from '../runtime/setup';
1616
import type {TestSnapshotResults} from '../runtime/snapshotContext';
17+
import type {BenchmarkTestArtifact} from './benchmarkUtils';
1718
import type {FantomTestConfig} from './getFantomTestConfigs';
1819
import type {
1920
AsyncCommandResult,
2021
ConsoleLogMessage,
2122
HermesVariant,
2223
} from './utils';
2324

25+
import {printBenchmarkResultsRanking} from './benchmarkUtils';
2426
import {createBundle, createSourceMap} from './bundling';
2527
import entrypointTemplate from './entrypoint-template';
2628
import * as EnvironmentOptions from './EnvironmentOptions';
@@ -233,6 +235,7 @@ module.exports = async function runTest(
233235
snapshotResults: {} as TestSnapshotResults,
234236
status: 'pending' as TestCaseResult['status'],
235237
testFilePath: testPath,
238+
testArtifact: {} as mixed,
236239
title,
237240
},
238241
];
@@ -431,6 +434,18 @@ module.exports = async function runTest(
431434
snapshotResults,
432435
);
433436

437+
printBenchmarkResultsRanking(
438+
testResults.map(testResult => {
439+
// $FlowExpectedError[incompatible-cast]
440+
const testArtifact = testResult.testArtifact as ?BenchmarkTestArtifact;
441+
const title = testResult.ancestorTitles[0];
442+
return {
443+
title,
444+
testArtifact,
445+
};
446+
}),
447+
);
448+
434449
return {
435450
testFilePath: testPath,
436451
failureMessage: formatResultsErrors(

private/react-native-fantom/runtime/setup.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type TestCaseResult = {
2626
failureDetails: Array<FailureDetail>,
2727
numPassingAsserts: number,
2828
snapshotResults: TestSnapshotResults,
29+
testArtifact?: mixed,
2930
// location: string,
3031
};
3132

@@ -314,6 +315,7 @@ function runSpec(spec: Spec): TestCaseResult {
314315
failureDetails: [],
315316
numPassingAsserts: 0,
316317
snapshotResults: {},
318+
testArtifact: null,
317319
};
318320

319321
if (!shouldRunSuite(spec)) {
@@ -328,7 +330,7 @@ function runSpec(spec: Spec): TestCaseResult {
328330

329331
try {
330332
invokeHooks(spec.parentContext, 'beforeEachHooks');
331-
spec.implementation();
333+
result.testArtifact = spec.implementation();
332334
invokeHooks(spec.parentContext, 'afterEachHooks');
333335

334336
status = 'passed';

private/react-native-fantom/src/Benchmark.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ export function suite(
176176
'Failing focused test to prevent it from being committed',
177177
);
178178
}
179+
return createBenchmarkTestArtifact(bench, tasks);
179180
});
180181

181182
const test = (
@@ -253,3 +254,17 @@ function printBenchmarkResults(bench: Bench) {
253254
console.table(nullthrows(bench.table()));
254255
console.log('');
255256
}
257+
258+
function createBenchmarkTestArtifact(bench: Bench, tasks: Array<TestTask>) {
259+
return {
260+
type: 'benchmark',
261+
timings: tasks.map((task, i) => {
262+
const result = bench.results[i];
263+
const {min, max, mean, p50, p75, p99} = result.latency;
264+
return {
265+
name: task.name,
266+
latency: {min, max, mean, p50, p75, p99},
267+
};
268+
}),
269+
};
270+
}

0 commit comments

Comments
 (0)