| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /// An entrypoint used to run portions of analyzer and measure its performance. | 5 /// An entrypoint used to run portions of analyzer and measure its performance. |
| 6 library analyzer_cli.tool.perf; | 6 library analyzer_cli.tool.perf; |
| 7 | 7 |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 import 'dart:io' show exit; | 9 import 'dart:io' show exit; |
| 10 | 10 |
| 11 import 'package:analyzer/dart/ast/ast.dart'; | 11 import 'package:analyzer/dart/ast/ast.dart'; |
| 12 import 'package:analyzer/dart/ast/token.dart'; | 12 import 'package:analyzer/dart/ast/token.dart'; |
| 13 import 'package:analyzer/error/listener.dart'; | 13 import 'package:analyzer/error/listener.dart'; |
| 14 import 'package:analyzer/file_system/file_system.dart' show ResourceUriResolver; | 14 import 'package:analyzer/file_system/file_system.dart' show ResourceUriResolver; |
| 15 import 'package:analyzer/file_system/physical_file_system.dart' | 15 import 'package:analyzer/file_system/physical_file_system.dart' |
| 16 show PhysicalResourceProvider; | 16 show PhysicalResourceProvider; |
| 17 import 'package:analyzer/source/package_map_resolver.dart'; | 17 import 'package:analyzer/source/package_map_resolver.dart'; |
| 18 import 'package:analyzer/src/context/builder.dart'; | 18 import 'package:analyzer/src/context/builder.dart'; |
| 19 import 'package:analyzer/src/dart/scanner/reader.dart'; | 19 import 'package:analyzer/src/dart/scanner/reader.dart'; |
| 20 import 'package:analyzer/src/dart/scanner/scanner.dart'; | 20 import 'package:analyzer/src/dart/scanner/scanner.dart'; |
| 21 import 'package:analyzer/src/dart/sdk/sdk.dart' show FolderBasedDartSdk; | 21 import 'package:analyzer/src/dart/sdk/sdk.dart' show FolderBasedDartSdk; |
| 22 import 'package:analyzer/src/generated/parser.dart'; | 22 import 'package:analyzer/src/generated/parser.dart'; |
| 23 import 'package:analyzer/src/generated/source.dart'; | 23 import 'package:analyzer/src/generated/source.dart'; |
| 24 import 'package:analyzer/src/generated/source_io.dart'; | 24 import 'package:analyzer/src/generated/source_io.dart'; |
| 25 import 'package:package_config/discovery.dart'; | 25 import 'package:package_config/discovery.dart'; |
| 26 | 26 |
| 27 /// Cumulative total number of chars scanned. | |
| 28 int scanTotalChars = 0; | |
| 29 | |
| 30 /// Cumulative time spent scanning. | |
| 31 Stopwatch scanTimer = new Stopwatch(); | |
| 32 | |
| 33 /// Factory to load and resolve app, packages, and sdk sources. | |
| 34 SourceFactory sources; | |
| 35 | |
| 36 main(List<String> args) async { | 27 main(List<String> args) async { |
| 37 // TODO(sigmund): provide sdk folder as well. | 28 // TODO(sigmund): provide sdk folder as well. |
| 38 if (args.length < 2) { | 29 if (args.length < 2) { |
| 39 print('usage: perf.dart <bench-id> <entry.dart>'); | 30 print('usage: perf.dart <bench-id> <entry.dart>'); |
| 40 exit(1); | 31 exit(1); |
| 41 } | 32 } |
| 42 var totalTimer = new Stopwatch()..start(); | 33 var totalTimer = new Stopwatch()..start(); |
| 43 | 34 |
| 44 var bench = args[0]; | 35 var bench = args[0]; |
| 45 var entryUri = Uri.base.resolve(args[1]); | 36 var entryUri = Uri.base.resolve(args[1]); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 57 } else { | 48 } else { |
| 58 print('unsupported bench-id: $bench. Please specify "scan" or "parse"'); | 49 print('unsupported bench-id: $bench. Please specify "scan" or "parse"'); |
| 59 // TODO(sigmund): implement the remaining benchmarks. | 50 // TODO(sigmund): implement the remaining benchmarks. |
| 60 exit(1); | 51 exit(1); |
| 61 } | 52 } |
| 62 | 53 |
| 63 totalTimer.stop(); | 54 totalTimer.stop(); |
| 64 report("total", totalTimer.elapsedMicroseconds); | 55 report("total", totalTimer.elapsedMicroseconds); |
| 65 } | 56 } |
| 66 | 57 |
| 67 /// Sets up analyzer to be able to load and resolve app, packages, and sdk | 58 /// Cumulative time spent scanning. |
| 68 /// sources. | 59 Stopwatch scanTimer = new Stopwatch(); |
| 69 Future setup(Uri entryUri) async { | 60 |
| 70 var provider = PhysicalResourceProvider.INSTANCE; | 61 /// Cumulative total number of chars scanned. |
| 71 var packageMap = new ContextBuilder(provider, null, null) | 62 int scanTotalChars = 0; |
| 72 .convertPackagesToMap(await findPackages(entryUri)); | 63 |
| 73 sources = new SourceFactory([ | 64 /// Factory to load and resolve app, packages, and sdk sources. |
| 74 new ResourceUriResolver(provider), | 65 SourceFactory sources; |
| 75 new PackageMapUriResolver(provider, packageMap), | 66 |
| 76 new DartUriResolver( | 67 /// Add to [files] all sources reachable from [start]. |
| 77 new FolderBasedDartSdk(provider, provider.getFolder("sdk"))), | 68 void collectSources(Source start, Set<Source> files) { |
| 78 ]); | 69 if (!files.add(start)) return; |
| 70 var unit = parseDirectives(start); |
| 71 for (var directive in unit.directives) { |
| 72 if (directive is UriBasedDirective) { |
| 73 var next = sources.resolveUri(start, directive.uri.stringValue); |
| 74 collectSources(next, files); |
| 75 } |
| 76 } |
| 79 } | 77 } |
| 80 | 78 |
| 81 /// Load and scans all files we need to process: files reachable from the | 79 /// Uses the diet-parser to parse only directives in [source]. |
| 82 /// entrypoint and all core libraries automatically included by the VM. | 80 CompilationUnit parseDirectives(Source source) { |
| 83 Set<Source> scanReachableFiles(Uri entryUri) { | 81 var token = tokenize(source); |
| 84 var files = new Set<Source>(); | 82 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); |
| 85 var loadTimer = new Stopwatch()..start(); | 83 return parser.parseDirectives(token); |
| 86 collectSources(sources.forUri2(entryUri), files); | |
| 87 | |
| 88 var libs = [ | |
| 89 "dart:async", | |
| 90 "dart:collection", | |
| 91 "dart:convert", | |
| 92 "dart:core", | |
| 93 "dart:developer", | |
| 94 "dart:_internal", | |
| 95 "dart:isolate", | |
| 96 "dart:math", | |
| 97 "dart:mirrors", | |
| 98 "dart:typed_data", | |
| 99 "dart:io" | |
| 100 ]; | |
| 101 | |
| 102 for (var lib in libs) { | |
| 103 collectSources(sources.forUri(lib), files); | |
| 104 } | |
| 105 | |
| 106 loadTimer.stop(); | |
| 107 | |
| 108 print('input size: ${scanTotalChars} chars'); | |
| 109 var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; | |
| 110 report("load", loadTime); | |
| 111 report("scan", scanTimer.elapsedMicroseconds); | |
| 112 return files; | |
| 113 } | 84 } |
| 114 | 85 |
| 115 /// Scans every file in [files] and reports the time spent doing so. | 86 /// Parses every file in [files] and reports the time spent doing so. |
| 116 void scanFiles(Set<Source> files) { | 87 void parseFiles(Set<Source> files) { |
| 117 // The code below will record again how many chars are scanned and how long it | 88 // The code below will record again how many chars are scanned and how long it |
| 118 // takes to scan them, even though we already did so in [scanReachableFiles]. | 89 // takes to scan them, even though we already did so in [scanReachableFiles]. |
| 119 // Recording and reporting this twice is unnecessary, but we do so for now to | 90 // Recording and reporting this twice is unnecessary, but we do so for now to |
| 120 // validate that the results are consistent. | 91 // validate that the results are consistent. |
| 121 scanTimer = new Stopwatch(); | 92 scanTimer = new Stopwatch(); |
| 122 var old = scanTotalChars; | 93 var old = scanTotalChars; |
| 123 scanTotalChars = 0; | 94 scanTotalChars = 0; |
| 95 var parseTimer = new Stopwatch()..start(); |
| 124 for (var source in files) { | 96 for (var source in files) { |
| 125 tokenize(source); | 97 parseFull(source); |
| 126 } | 98 } |
| 99 parseTimer.stop(); |
| 127 | 100 |
| 128 // Report size and scanning time again. See discussion above. | 101 // Report size and scanning time again. See discussion above. |
| 129 if (old != scanTotalChars) print('input size changed? ${old} chars'); | 102 if (old != scanTotalChars) print('input size changed? ${old} chars'); |
| 130 report("scan", scanTimer.elapsedMicroseconds); | 103 report("scan", scanTimer.elapsedMicroseconds); |
| 104 |
| 105 var pTime = parseTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; |
| 106 report("parse", pTime); |
| 131 } | 107 } |
| 132 | 108 |
| 133 /// Parses every file in [files] and reports the time spent doing so. | 109 /// Parse the full body of [source] and return it's compilation unit. |
| 134 void parseFiles(Set<Source> files) { | 110 CompilationUnit parseFull(Source source) { |
| 111 var token = tokenize(source); |
| 112 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); |
| 113 return parser.parseCompilationUnit(token); |
| 114 } |
| 115 |
| 116 /// Report that metric [name] took [time] micro-seconds to process |
| 117 /// [scanTotalChars] characters. |
| 118 void report(String name, int time) { |
| 119 var sb = new StringBuffer(); |
| 120 sb.write('$name: $time us, ${time ~/ 1000} ms'); |
| 121 sb.write(', ${scanTotalChars * 1000 ~/ time} chars/ms'); |
| 122 print('$sb'); |
| 123 } |
| 124 |
| 125 /// Scans every file in [files] and reports the time spent doing so. |
| 126 void scanFiles(Set<Source> files) { |
| 135 // The code below will record again how many chars are scanned and how long it | 127 // The code below will record again how many chars are scanned and how long it |
| 136 // takes to scan them, even though we already did so in [scanReachableFiles]. | 128 // takes to scan them, even though we already did so in [scanReachableFiles]. |
| 137 // Recording and reporting this twice is unnecessary, but we do so for now to | 129 // Recording and reporting this twice is unnecessary, but we do so for now to |
| 138 // validate that the results are consistent. | 130 // validate that the results are consistent. |
| 139 scanTimer = new Stopwatch(); | 131 scanTimer = new Stopwatch(); |
| 140 var old = scanTotalChars; | 132 var old = scanTotalChars; |
| 141 scanTotalChars = 0; | 133 scanTotalChars = 0; |
| 142 var parseTimer = new Stopwatch()..start(); | |
| 143 for (var source in files) { | 134 for (var source in files) { |
| 144 parseFull(source); | 135 tokenize(source); |
| 145 } | 136 } |
| 146 parseTimer.stop(); | |
| 147 | 137 |
| 148 // Report size and scanning time again. See discussion above. | 138 // Report size and scanning time again. See discussion above. |
| 149 if (old != scanTotalChars) print('input size changed? ${old} chars'); | 139 if (old != scanTotalChars) print('input size changed? ${old} chars'); |
| 150 report("scan", scanTimer.elapsedMicroseconds); | 140 report("scan", scanTimer.elapsedMicroseconds); |
| 151 | |
| 152 var pTime = parseTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; | |
| 153 report("parse", pTime); | |
| 154 } | 141 } |
| 155 | 142 |
| 156 /// Add to [files] all sources reachable from [start]. | 143 /// Load and scans all files we need to process: files reachable from the |
| 157 void collectSources(Source start, Set<Source> files) { | 144 /// entrypoint and all core libraries automatically included by the VM. |
| 158 if (!files.add(start)) return; | 145 Set<Source> scanReachableFiles(Uri entryUri) { |
| 159 var unit = parseDirectives(start); | 146 var files = new Set<Source>(); |
| 160 for (var directive in unit.directives) { | 147 var loadTimer = new Stopwatch()..start(); |
| 161 if (directive is UriBasedDirective) { | 148 collectSources(sources.forUri2(entryUri), files); |
| 162 var next = sources.resolveUri(start, directive.uri.stringValue); | 149 |
| 163 collectSources(next, files); | 150 var libs = [ |
| 164 } | 151 "dart:async", |
| 152 "dart:collection", |
| 153 "dart:convert", |
| 154 "dart:core", |
| 155 "dart:developer", |
| 156 "dart:_internal", |
| 157 "dart:isolate", |
| 158 "dart:math", |
| 159 "dart:mirrors", |
| 160 "dart:typed_data", |
| 161 "dart:io" |
| 162 ]; |
| 163 |
| 164 for (var lib in libs) { |
| 165 collectSources(sources.forUri(lib), files); |
| 165 } | 166 } |
| 167 |
| 168 loadTimer.stop(); |
| 169 |
| 170 print('input size: ${scanTotalChars} chars'); |
| 171 var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; |
| 172 report("load", loadTime); |
| 173 report("scan", scanTimer.elapsedMicroseconds); |
| 174 return files; |
| 166 } | 175 } |
| 167 | 176 |
| 168 /// Uses the diet-parser to parse only directives in [source]. | 177 /// Sets up analyzer to be able to load and resolve app, packages, and sdk |
| 169 CompilationUnit parseDirectives(Source source) { | 178 /// sources. |
| 170 var token = tokenize(source); | 179 Future setup(Uri entryUri) async { |
| 171 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); | 180 var provider = PhysicalResourceProvider.INSTANCE; |
| 172 return parser.parseDirectives(token); | 181 var packageMap = new ContextBuilder(provider, null, null) |
| 173 } | 182 .convertPackagesToMap(await findPackages(entryUri)); |
| 174 | 183 sources = new SourceFactory([ |
| 175 /// Parse the full body of [source] and return it's compilation unit. | 184 new ResourceUriResolver(provider), |
| 176 CompilationUnit parseFull(Source source) { | 185 new PackageMapUriResolver(provider, packageMap), |
| 177 var token = tokenize(source); | 186 new DartUriResolver( |
| 178 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); | 187 new FolderBasedDartSdk(provider, provider.getFolder("sdk"))), |
| 179 return parser.parseCompilationUnit(token); | 188 ]); |
| 180 } | 189 } |
| 181 | 190 |
| 182 /// Scan [source] and return the first token produced by the scanner. | 191 /// Scan [source] and return the first token produced by the scanner. |
| 183 Token tokenize(Source source) { | 192 Token tokenize(Source source) { |
| 184 scanTimer.start(); | 193 scanTimer.start(); |
| 185 var contents = source.contents.data; | 194 var contents = source.contents.data; |
| 186 scanTotalChars += contents.length; | 195 scanTotalChars += contents.length; |
| 187 // TODO(sigmund): is there a way to scan from a random-access-file without | 196 // TODO(sigmund): is there a way to scan from a random-access-file without |
| 188 // first converting to String? | 197 // first converting to String? |
| 189 var scanner = new Scanner(source, new CharSequenceReader(contents), | 198 var scanner = new Scanner(source, new CharSequenceReader(contents), |
| 190 AnalysisErrorListener.NULL_LISTENER) | 199 AnalysisErrorListener.NULL_LISTENER) |
| 191 ..preserveComments = false; | 200 ..preserveComments = false; |
| 192 var token = scanner.tokenize(); | 201 var token = scanner.tokenize(); |
| 193 scanTimer.stop(); | 202 scanTimer.stop(); |
| 194 return token; | 203 return token; |
| 195 } | 204 } |
| 196 | |
| 197 /// Report that metric [name] took [time] micro-seconds to process | |
| 198 /// [scanTotalChars] characters. | |
| 199 void report(String name, int time) { | |
| 200 var sb = new StringBuffer(); | |
| 201 sb.write('$name: $time us, ${time ~/ 1000} ms'); | |
| 202 sb.write(', ${scanTotalChars * 1000 ~/ time} chars/ms'); | |
| 203 print('$sb'); | |
| 204 } | |
| OLD | NEW |