-
Notifications
You must be signed in to change notification settings - Fork 57
FED-4308 Add dart2js output benchmarks #991
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
d7ebc69
Rename existing benchmark file
greglittlefield-wf 0e6f936
Add dart2js output benchmarks
greglittlefield-wf b1d8b85
Improve output when there is no difference
greglittlefield-wf ebcd9c2
Colorize diffs in benchmark
greglittlefield-wf c1ddc4f
Turn colorization back off, since it interferes with piping output
greglittlefield-wf 9a2681d
Deep-sort lists and maps in dart2js normalization
greglittlefield-wf 57e39e3
Fix ResizeSensor/DomProps affecting component size diff
greglittlefield-wf dc9f72e
Conditionally color output when using a terminal
greglittlefield-wf ed81cef
Format
greglittlefield-wf e56a07c
Revert "Fix ResizeSensor/DomProps affecting component size diff"
greglittlefield-wf cf5b4db
Merge remote-tracking branch 'origin/master' into dart2js-output-benc…
greglittlefield-wf 005dc1f
Format
greglittlefield-wf 45ae3be
Add git shorthand for dependencies
greglittlefield-wf bcaf769
Output Dart version when compiling
greglittlefield-wf 750efc6
Clean up
greglittlefield-wf 35bfc98
Merge remote-tracking branch 'origin/master' into dart2js-output-benc…
greglittlefield-wf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
File renamed without changes.
This file contains hidden or 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,263 @@ | ||
| import 'dart:io'; | ||
|
|
||
| import 'package:args/command_runner.dart'; | ||
| import 'package:meta/meta.dart'; | ||
|
|
||
| import 'dart2js_output/compile.dart'; | ||
| import 'dart2js_output/dart2js_normalize.dart'; | ||
| import 'dart2js_output/dependency.dart'; | ||
| import 'dart2js_output/logging.dart'; | ||
| import 'dart2js_output/source.dart' as source; | ||
|
|
||
| Future<void> main(List<String> args) async { | ||
| final runner = CommandRunner( | ||
| 'benchmark-dart2js-output', | ||
| 'Runs various dart2js output benchmarks and comparisons.' | ||
| ' Useful for debugging/validating how changes to over_react affect dart2js output.') | ||
| ..addCommand(CompareSizeCommand()) | ||
| ..addCommand(CompareCodeCommand()) | ||
| ..addCommand(GetCodeCommand()); | ||
|
|
||
| await runner.run(args); | ||
| } | ||
|
|
||
| abstract class BaseCommand extends Command { | ||
| BaseCommand() { | ||
| argParser.addFlag('verbose', defaultsTo: true, negatable: true); | ||
| } | ||
|
|
||
| @override | ||
| @mustCallSuper | ||
| void run() { | ||
| initLogging(verbose: argResults!['verbose'] as bool); | ||
| } | ||
| } | ||
|
|
||
| abstract class CompareCommand extends BaseCommand { | ||
| CompareCommand() { | ||
| argParser.addOption( | ||
| 'head', | ||
| help: 'Head over_react dependency to compare to the base.' | ||
| ' Supports dependencies entry in JSON format, or `git:<ref>` shorthand.' | ||
| ' Defaults to the enclosing local working copy of over_react.', | ||
| defaultsTo: localPathDepString, | ||
| ); | ||
| argParser.addOption( | ||
| 'base', | ||
| help: 'Base over_react dependency to compare against.' | ||
| ' Supports dependencies entry in JSON format, or `git:<ref>` shorthand.', | ||
| defaultsTo: originHeadDepString, | ||
| ); | ||
| } | ||
|
|
||
| dynamic get _baseDep => parseOverReactDependency(argResults!['base'] as String); | ||
|
|
||
| dynamic get _headDep => parseOverReactDependency(argResults!['head'] as String); | ||
| } | ||
|
|
||
| class CompareSizeCommand extends CompareCommand { | ||
| @override | ||
| String get description => | ||
| 'Compares the optimized, minified size of dart2js output for a benchmark React component between two over_react versions.'; | ||
|
|
||
| @override | ||
| String get name => 'compare-size'; | ||
|
|
||
| @override | ||
| Future<void> run() async { | ||
| super.run(); | ||
| final baseSize = getComponentAndUsageSize(overReactDep: _baseDep); | ||
| final headSize = getComponentAndUsageSize(overReactDep: _headDep); | ||
| print('Base size: ${await baseSize} bytes'); | ||
| print('Head size: ${await headSize} bytes'); | ||
| print('(Head size) - (base size):' | ||
| ' ${(await headSize) - (await baseSize)} bytes'); | ||
| } | ||
| } | ||
|
|
||
| class CompareCodeCommand extends CompareCommand { | ||
| @override | ||
| String get name => 'compare-code'; | ||
|
|
||
| @override | ||
| String get description => | ||
| 'Compares the optimized, non-minified dart2js output for a benchmark React component between two over_react versions.' | ||
| '\nOutputs in a Git diff format.' | ||
| '\nCompiled code is normalized before comparison for better diffing.'; | ||
|
|
||
| @override | ||
| Future<void> run() async { | ||
| super.run(); | ||
| final diff = await compareCodeAcrossVersions( | ||
| source.componentBenchmark( | ||
| componentCount: 1, | ||
| propsCount: 5, | ||
| ), | ||
| overReactDep1: _baseDep, | ||
| overReactDep2: _headDep, | ||
| color: stdioType(stdout) == StdioType.terminal, | ||
| ); | ||
| if (diff.trim().isEmpty) { | ||
| print('(No difference in dart2js output between base and head)'); | ||
| } else { | ||
| print(diff); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| class GetCodeCommand extends BaseCommand { | ||
| @override | ||
| String get name => 'get-code'; | ||
|
|
||
| @override | ||
| String get description => | ||
| 'Displays the optimized, non-minified dart2js output for a benchmark React component.' | ||
| '\nOutputs in a Git diff format, showing output changes when adding a component.' | ||
| '\nCompiled code is normalized before comparison for better diffing.'; | ||
|
|
||
| GetCodeCommand() { | ||
| argParser.addOption( | ||
| 'dependency', | ||
| help: 'over_react dependency to compile with.' | ||
| ' Supports dependencies entry in JSON format, or `git:<ref>` shorthand.' | ||
| ' Defaults to the enclosing local working copy of over_react.', | ||
| defaultsTo: localPathDepString, | ||
| ); | ||
| } | ||
|
|
||
| dynamic get _dep => parseOverReactDependency(argResults!['dependency'] as String); | ||
|
|
||
| @override | ||
| Future<void> run() async { | ||
| super.run(); | ||
| print(await getCompiledComponentCode( | ||
| overReactDep: _dep, | ||
| color: stdioType(stdout) == StdioType.terminal, | ||
| )); | ||
| } | ||
| } | ||
|
|
||
| Future<String> compareCodeAcrossVersions( | ||
| String code, { | ||
| required dynamic overReactDep1, | ||
| required dynamic overReactDep2, | ||
| bool color = false, | ||
| }) async { | ||
| final results1 = compileOverReactProgram( | ||
| webFilesByName: {'main.dart': code}, | ||
| overReactDep: overReactDep1, | ||
| minify: false, | ||
| ); | ||
| final results2 = compileOverReactProgram( | ||
| webFilesByName: {'main.dart': code}, | ||
| overReactDep: overReactDep2, | ||
| minify: false, | ||
| ); | ||
|
|
||
| return gitDiffNoIndex( | ||
| createNormalizedDart2jsFile((await results1).getCompiledDart2jsFile()).path, | ||
| createNormalizedDart2jsFile((await results2).getCompiledDart2jsFile()).path, | ||
| color: color, | ||
| ); | ||
| } | ||
|
|
||
| Future<String> getCompiledComponentCode({ | ||
| dynamic overReactDep, | ||
| bool color = false, | ||
| }) async { | ||
| const baselineComponentCount = 2; | ||
| const propsCount = 3; | ||
|
|
||
| final result = await compileOverReactProgram(webFilesByName: { | ||
| 'baseline.dart': source.componentBenchmark( | ||
| componentCount: baselineComponentCount, | ||
| propsCount: propsCount, | ||
| ), | ||
| 'additional.dart': source.componentBenchmark( | ||
| componentCount: baselineComponentCount + 1, | ||
| propsCount: propsCount, | ||
| ), | ||
| }, overReactDep: overReactDep, minify: false); | ||
|
|
||
| final baselineCompiledFile = result.getCompiledDart2jsFile('baseline.dart'); | ||
| final additionalCompiledFile = result.getCompiledDart2jsFile('additional.dart'); | ||
|
|
||
| return gitDiffNoIndex( | ||
| createNormalizedDart2jsFile(baselineCompiledFile).path, | ||
| createNormalizedDart2jsFile(additionalCompiledFile).path, | ||
| color: color, | ||
| ); | ||
| } | ||
|
|
||
| File createNormalizedDart2jsFile(File f) { | ||
| return File(f.path + '.normalized.js') | ||
| ..writeAsStringSync(normalizeDart2jsContents(f.readAsStringSync())); | ||
| } | ||
|
|
||
| Future<String> gitDiffNoIndex( | ||
| String file1, | ||
| String file2, { | ||
| int contextLines = 1, | ||
| bool color = false, | ||
| }) async { | ||
| final result = await Process.run('git', [ | ||
| 'diff', | ||
| '--no-index', | ||
| '-U$contextLines', | ||
| if (color) '--color', | ||
| file1, | ||
| file2, | ||
| ]); | ||
|
|
||
| if (result.exitCode == 0 || result.exitCode == 1) { | ||
| return result.stdout as String; | ||
| } | ||
|
|
||
| throw Exception('Error diffing files. Exit code: ${result.exitCode} stderr: $stderr'); | ||
| } | ||
|
|
||
| /// Gets the total size of a single test component, plus usage that sets all props, | ||
| /// and render that reads all props. | ||
| /// | ||
| /// Since it contains this extra usage and render code, it's mainly useful when | ||
| /// comparing across versions, and shouldn't by itself be used as a number that | ||
| /// represents "the cost of declaring a component"." | ||
| Future<int> getComponentAndUsageSize({ | ||
| dynamic overReactDep, | ||
| }) async { | ||
| const baselineComponentCount = 100; | ||
| const propsCount = 5; | ||
|
|
||
| final result = await compileOverReactProgram(webFilesByName: { | ||
| 'baseline.dart': source.componentBenchmark( | ||
| componentCount: baselineComponentCount, | ||
| propsCount: propsCount, | ||
| ), | ||
| 'additional.dart': source.componentBenchmark( | ||
| componentCount: baselineComponentCount + 1, | ||
| propsCount: propsCount, | ||
| ), | ||
| }, overReactDep: overReactDep); | ||
|
|
||
| final baselineFileSize = result.getCompiledDart2jsFile('baseline.dart').statSync().size; | ||
| final additionalFileSize = result.getCompiledDart2jsFile('additional.dart').statSync().size; | ||
| validateFileSize(baselineFileSize); | ||
| validateFileSize(additionalFileSize); | ||
|
|
||
| return additionalFileSize - baselineFileSize; | ||
| } | ||
|
|
||
| void validateFileSize(int actualSizeInBytes) { | ||
| // Arbitrary minimum expected size to help ensure the test setup is correct. | ||
| // | ||
| // Value derived from the compiled size of the following Dart program: | ||
| // import 'package:over_react/over_react.dart'; | ||
| // void main() { Dom.div()(); } | ||
| const minimumExpectedSizeInBytes = 144339; | ||
|
|
||
| if (actualSizeInBytes < minimumExpectedSizeInBytes) { | ||
| throw Exception('Expected compiled size to be larger,' | ||
| ' at least $minimumExpectedSizeInBytes bytes.' | ||
| ' Was: $actualSizeInBytes bytes.'); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be mentioned somewhere in the readme or wherever so we remember we have this debugging tool or do we not care that much?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mm good thought, but yeah I don't care that much haha. This and the other benchmark are pretty niche and on aspects of the code that don't get worked on much
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay cool!