| // Copyright 2023 The Flutter Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. | |
| import 'dart:convert'; | |
| import 'dart:io'; | |
| import 'package:args/args.dart'; | |
| import 'package:web_benchmarks/analysis.dart'; | |
| import 'package:web_benchmarks/server.dart'; | |
| import '../test_infra/common.dart'; | |
| import '../test_infra/project_root_directory.dart'; | |
| import 'args.dart'; | |
| import 'compare_benchmarks.dart'; | |
| import 'utils.dart'; | |
| /// Runs the DevTools web benchmarks and reports the benchmark data. | |
| /// | |
| /// To see available arguments, run this script with the `-h` flag. | |
| Future<void> main(List<String> args) async { | |
| if (args.isNotEmpty && args.first == '-h') { | |
| stdout.writeln(_Args._buildArgParser().usage); | |
| return; | |
| } | |
| final benchmarkArgs = _Args(args); | |
| final results = await runBenchmarks( | |
| averageOf: benchmarkArgs.averageOf, | |
| useWasm: benchmarkArgs.useWasm, | |
| useBrowser: benchmarkArgs.useBrowser, | |
| ); | |
| printAndMaybeSaveResults( | |
| benchmarkResults: results, | |
| saveToFileLocation: benchmarkArgs.saveToFileLocation, | |
| ); | |
| maybeCompareToBaseline( | |
| benchmarkResults: results, | |
| baselineLocation: benchmarkArgs.baselineLocation, | |
| ); | |
| } | |
| /// Runs the DevTools benchmarks [averageOf] times and takes returns the average | |
| /// of the benchmark runs as a single [BenchmarkResults] object. | |
| Future<BenchmarkResults> runBenchmarks({ | |
| required int averageOf, | |
| required bool useWasm, | |
| required bool useBrowser, | |
| }) async { | |
| final benchmarkResults = <BenchmarkResults>[]; | |
| for (var i = 1; i <= averageOf; i++) { | |
| stdout.writeln('Starting web benchmark tests (run #$i) ...'); | |
| benchmarkResults.add( | |
| await serveWebBenchmark( | |
| benchmarkAppDirectory: projectRootDirectory(), | |
| entryPoint: generateBenchmarkEntryPoint(useWasm: useWasm), | |
| compilationOptions: useWasm | |
| ? const CompilationOptions.wasm() | |
| : const CompilationOptions.js(), | |
| treeShakeIcons: false, | |
| benchmarkPath: benchmarkPath(useWasm: useWasm), | |
| headless: !useBrowser, | |
| ), | |
| ); | |
| stdout.writeln('Web benchmark tests finished (run #$i).'); | |
| } | |
| late final BenchmarkResults taskResult; | |
| if (averageOf == 1) { | |
| taskResult = benchmarkResults.first; | |
| } else { | |
| stdout.writeln( | |
| 'Taking the average of ${benchmarkResults.length} benchmark runs.', | |
| ); | |
| taskResult = computeAverage(benchmarkResults); | |
| } | |
| return taskResult; | |
| } | |
| /// Prints the [benchmarkResults] to stdout and optionally saves the results to | |
| /// disk at [saveToFileLocation] when this value is non-null. | |
| void printAndMaybeSaveResults({ | |
| required BenchmarkResults benchmarkResults, | |
| required String? saveToFileLocation, | |
| }) { | |
| final resultsAsMap = benchmarkResults.toJson(); | |
| final resultsAsJsonString = const JsonEncoder.withIndent( | |
| ' ', | |
| ).convert(resultsAsMap); | |
| if (saveToFileLocation != null) { | |
| final location = Uri.parse(saveToFileLocation); | |
| File.fromUri(location) | |
| ..createSync() | |
| ..writeAsStringSync(resultsAsJsonString); | |
| } | |
| stdout | |
| ..writeln('==== Results ====') | |
| ..writeln(resultsAsJsonString) | |
| ..writeln('==== End of results ====') | |
| ..writeln(); | |
| } | |
| /// Compares [benchmarkResults] to the benchmark results contained at the | |
| /// [baselineLocation] absolute file path, if they exist. | |
| /// | |
| /// This method computes a diff of the two benchmark runs and prints the delta | |
| /// information to stdout. | |
| void maybeCompareToBaseline({ | |
| required BenchmarkResults benchmarkResults, | |
| required String? baselineLocation, | |
| }) { | |
| if (baselineLocation != null) { | |
| final baselineFile = checkFileExists(baselineLocation); | |
| if (baselineFile != null) { | |
| final baselineResults = BenchmarkResults.parse( | |
| jsonDecode(baselineFile.readAsStringSync()), | |
| ); | |
| compareBenchmarks( | |
| baselineResults, | |
| benchmarkResults, | |
| baselineSource: baselineLocation, | |
| ); | |
| } | |
| } | |
| } | |
| class _Args extends BenchmarkArgsBase { | |
| _Args(List<String> args) { | |
| init(args, parser: _buildArgParser()); | |
| } | |
| bool get useBrowser => argResults[BenchmarkArgument.browser.flagName]; | |
| bool get useWasm => argResults[BenchmarkArgument.wasm.flagName]; | |
| static ArgParser _buildArgParser() { | |
| return ArgParser() | |
| ..addSaveToFileOption(BenchmarkResultsOutputType.json) | |
| ..addAverageOfOption() | |
| ..addBaselineOption() | |
| ..addFlag( | |
| BenchmarkArgument.browser.flagName, | |
| negatable: false, | |
| help: 'Runs the benchmark tests in browser mode (not headless mode).', | |
| ) | |
| ..addFlag( | |
| BenchmarkArgument.wasm.flagName, | |
| negatable: false, | |
| help: 'Runs the benchmark tests with dart2wasm', | |
| ); | |
| } | |
| } |