OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /// An entrypoint used to run portions of dart2js and measure its performance. |
| 6 library compiler.tool.perf; |
| 7 |
| 8 import 'dart:async'; |
| 9 import 'dart:io'; |
| 10 |
| 11 import 'package:compiler/compiler_new.dart'; |
| 12 import 'package:compiler/src/common.dart'; |
| 13 import 'package:compiler/src/diagnostics/diagnostic_listener.dart'; |
| 14 import 'package:compiler/src/diagnostics/messages.dart' |
| 15 show Message, MessageTemplate; |
| 16 import 'package:compiler/src/io/source_file.dart'; |
| 17 import 'package:compiler/src/options.dart' show ParserOptions; |
| 18 import 'package:compiler/src/options.dart'; |
| 19 import 'package:compiler/src/parser/element_listener.dart' show ScannerOptions; |
| 20 import 'package:compiler/src/parser/listener.dart'; |
| 21 import 'package:compiler/src/parser/node_listener.dart' show NodeListener; |
| 22 import 'package:compiler/src/parser/parser.dart' show Parser; |
| 23 import 'package:compiler/src/parser/partial_parser.dart'; |
| 24 import 'package:compiler/src/platform_configuration.dart' as platform; |
| 25 import 'package:compiler/src/scanner/scanner.dart'; |
| 26 import 'package:compiler/src/source_file_provider.dart'; |
| 27 import 'package:compiler/src/tokens/token.dart' show Token; |
| 28 import 'package:package_config/discovery.dart' show findPackages; |
| 29 import 'package:package_config/packages.dart' show Packages; |
| 30 import 'package:package_config/src/util.dart' show checkValidPackageUri; |
| 31 |
| 32 /// Cumulative total number of chars scanned. |
| 33 int scanTotalChars = 0; |
| 34 |
| 35 /// Cumulative time spent scanning. |
| 36 Stopwatch scanTimer = new Stopwatch(); |
| 37 |
| 38 /// Helper class used to load source files using dart2js's internal APIs. |
| 39 _Loader loader; |
| 40 |
| 41 main(List<String> args) async { |
| 42 // TODO(sigmund): provide sdk folder as well. |
| 43 if (args.length < 2) { |
| 44 print('usage: perf.dart <bench-id> <entry.dart>'); |
| 45 exit(1); |
| 46 } |
| 47 var totalTimer = new Stopwatch()..start(); |
| 48 |
| 49 var bench = args[0]; |
| 50 var entryUri = Uri.base.resolve(args[1]); |
| 51 |
| 52 await setup(entryUri); |
| 53 |
| 54 if (bench == 'scan') { |
| 55 Set<SourceFile> files = await scanReachableFiles(entryUri); |
| 56 // TODO(sigmund): consider replacing the warmup with instrumented snapshots. |
| 57 for (int i = 0; i < 10; i++) scanFiles(files); |
| 58 } else if (bench == 'parse') { |
| 59 Set<SourceFile> files = await scanReachableFiles(entryUri); |
| 60 // TODO(sigmund): consider replacing the warmup with instrumented snapshots. |
| 61 for (int i = 0; i < 10; i++) parseFiles(files); |
| 62 } else { |
| 63 print('unsupported bench-id: $bench. Please specify "scan" or "parse"'); |
| 64 // TODO(sigmund): implement the remaining benchmarks. |
| 65 exit(1); |
| 66 } |
| 67 |
| 68 totalTimer.stop(); |
| 69 report("total", totalTimer.elapsedMicroseconds); |
| 70 } |
| 71 |
| 72 Future setup(Uri entryUri) async { |
| 73 var inputProvider = new CompilerSourceFileProvider(); |
| 74 var sdkLibraries = await platform.load(_platformConfigUri, inputProvider); |
| 75 var packages = await findPackages(entryUri); |
| 76 loader = new _Loader(inputProvider, sdkLibraries, packages); |
| 77 } |
| 78 |
| 79 /// Load and scans all files we need to process: files reachable from the |
| 80 /// entrypoint and all core libraries automatically included by the VM. |
| 81 Future<Set<SourceFile>> scanReachableFiles(Uri entryUri) async { |
| 82 var files = new Set<SourceFile>(); |
| 83 var loadTimer = new Stopwatch()..start(); |
| 84 var entrypoints = [ |
| 85 entryUri, |
| 86 Uri.parse("dart:async"), |
| 87 Uri.parse("dart:collection"), |
| 88 Uri.parse("dart:convert"), |
| 89 Uri.parse("dart:core"), |
| 90 Uri.parse("dart:developer"), |
| 91 Uri.parse("dart:_internal"), |
| 92 Uri.parse("dart:io"), |
| 93 Uri.parse("dart:isolate"), |
| 94 Uri.parse("dart:math"), |
| 95 Uri.parse("dart:mirrors"), |
| 96 Uri.parse("dart:typed_data"), |
| 97 ]; |
| 98 for (var entry in entrypoints) { |
| 99 await collectSources(await loader.loadFile(entry), files); |
| 100 } |
| 101 loadTimer.stop(); |
| 102 |
| 103 print('input size: ${scanTotalChars} chars'); |
| 104 var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; |
| 105 report("load", loadTime); |
| 106 report("scan", scanTimer.elapsedMicroseconds); |
| 107 return files; |
| 108 } |
| 109 |
| 110 /// Scans every file in [files] and reports the time spent doing so. |
| 111 void scanFiles(Set<SourceFile> files) { |
| 112 // The code below will record again how many chars are scanned and how long it |
| 113 // takes to scan them, even though we already did so in [scanReachableFiles]. |
| 114 // Recording and reporting this twice is unnecessary, but we do so for now to |
| 115 // validate that the results are consistent. |
| 116 scanTimer = new Stopwatch(); |
| 117 var old = scanTotalChars; |
| 118 scanTotalChars = 0; |
| 119 for (var source in files) { |
| 120 tokenize(source); |
| 121 } |
| 122 |
| 123 // Report size and scanning time again. See discussion above. |
| 124 if (old != scanTotalChars) print('input size changed? ${old} chars'); |
| 125 report("scan", scanTimer.elapsedMicroseconds); |
| 126 } |
| 127 |
| 128 /// Parses every file in [files] and reports the time spent doing so. |
| 129 void parseFiles(Set<SourceFile> files) { |
| 130 // The code below will record again how many chars are scanned and how long it |
| 131 // takes to scan them, even though we already did so in [scanReachableFiles]. |
| 132 // Recording and reporting this twice is unnecessary, but we do so for now to |
| 133 // validate that the results are consistent. |
| 134 scanTimer = new Stopwatch(); |
| 135 var old = scanTotalChars; |
| 136 scanTotalChars = 0; |
| 137 var parseTimer = new Stopwatch()..start(); |
| 138 for (var source in files) { |
| 139 parseFull(source); |
| 140 } |
| 141 parseTimer.stop(); |
| 142 |
| 143 // Report size and scanning time again. See discussion above. |
| 144 if (old != scanTotalChars) print('input size changed? ${old} chars'); |
| 145 report("scan", scanTimer.elapsedMicroseconds); |
| 146 |
| 147 report( |
| 148 "parse", parseTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds); |
| 149 } |
| 150 |
| 151 /// Add to [files] all sources reachable from [start]. |
| 152 Future collectSources(SourceFile start, Set<SourceFile> files) async { |
| 153 if (!files.add(start)) return; |
| 154 for (var directive in parseDirectives(start)) { |
| 155 var next = await loader.loadFile(start.uri.resolve(directive)); |
| 156 await collectSources(next, files); |
| 157 } |
| 158 } |
| 159 |
| 160 /// Uses the diet-parser to parse only directives in [source], returns the |
| 161 /// URIs seen in import/export/part directives in the file. |
| 162 Set<String> parseDirectives(SourceFile source) { |
| 163 var tokens = tokenize(source); |
| 164 var listener = new DirectiveListener(); |
| 165 new PartialParser(listener, const _ParserOptions()).parseUnit(tokens); |
| 166 return listener.targets; |
| 167 } |
| 168 |
| 169 /// Parse the full body of [source]. |
| 170 parseFull(SourceFile source) { |
| 171 var tokens = tokenize(source); |
| 172 NodeListener listener = new NodeListener( |
| 173 const ScannerOptions(canUseNative: true), new FakeReporter(), null); |
| 174 Parser parser = new Parser(listener, const _ParserOptions()); |
| 175 parser.parseUnit(tokens); |
| 176 return listener.popNode(); |
| 177 } |
| 178 |
| 179 /// Scan [source] and return the first token produced by the scanner. |
| 180 Token tokenize(SourceFile source) { |
| 181 scanTimer.start(); |
| 182 scanTotalChars += source.length; |
| 183 var token = new Scanner(source).tokenize(); |
| 184 scanTimer.stop(); |
| 185 return token; |
| 186 } |
| 187 |
| 188 /// Report that metric [name] took [time] micro-seconds to process |
| 189 /// [scanTotalChars] characters. |
| 190 void report(String name, int time) { |
| 191 var sb = new StringBuffer(); |
| 192 sb.write('$name: $time us, ${time ~/ 1000} ms'); |
| 193 sb.write(', ${scanTotalChars * 1000 ~/ time} chars/ms'); |
| 194 print('$sb'); |
| 195 } |
| 196 |
| 197 /// Listener that parses out just the uri in imports, exports, and part |
| 198 /// directives. |
| 199 class DirectiveListener extends Listener { |
| 200 Set<String> targets = new Set<String>(); |
| 201 |
| 202 bool inDirective = false; |
| 203 void enterDirective() { |
| 204 inDirective = true; |
| 205 } |
| 206 |
| 207 void exitDirective() { |
| 208 inDirective = false; |
| 209 } |
| 210 |
| 211 void beginImport(Token importKeyword) => enterDirective(); |
| 212 void beginExport(Token token) => enterDirective(); |
| 213 void beginPart(Token token) => enterDirective(); |
| 214 |
| 215 void beginLiteralString(Token token) { |
| 216 if (inDirective) { |
| 217 var quotedString = token.value; |
| 218 targets.add(quotedString.substring(1, quotedString.length - 1)); |
| 219 } |
| 220 } |
| 221 |
| 222 void endExport(Token exportKeyword, Token semicolon) => exitDirective(); |
| 223 void endImport(Token importKeyword, Token deferredKeyword, Token asKeyword, |
| 224 Token semicolon) => |
| 225 exitDirective(); |
| 226 void endPart(Token partKeyword, Token semicolon) => exitDirective(); |
| 227 } |
| 228 |
| 229 Uri _libraryRoot = Platform.script.resolve('../../../sdk/'); |
| 230 Uri _platformConfigUri = _libraryRoot.resolve("lib/dart_server.platform"); |
| 231 |
| 232 class FakeReporter extends DiagnosticReporter { |
| 233 final hasReportedError = false; |
| 234 final options = new FakeReporterOptions(); |
| 235 |
| 236 withCurrentElement(e, f) => f(); |
| 237 log(m) => print(m); |
| 238 internalError(_, m) => print(m); |
| 239 spanFromSpannable(_) => null; |
| 240 |
| 241 void reportError(DiagnosticMessage message, |
| 242 [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) { |
| 243 print('error: ${message.message}'); |
| 244 } |
| 245 |
| 246 void reportWarning(DiagnosticMessage message, |
| 247 [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) { |
| 248 print('warning: ${message.message}'); |
| 249 } |
| 250 |
| 251 void reportHint(DiagnosticMessage message, |
| 252 [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) { |
| 253 print('hint: ${message.message}'); |
| 254 } |
| 255 |
| 256 void reportInfo(_, __, [Map arguments = const {}]) {} |
| 257 |
| 258 DiagnosticMessage createMessage(_, MessageKind kind, |
| 259 [Map arguments = const {}]) { |
| 260 MessageTemplate template = MessageTemplate.TEMPLATES[kind]; |
| 261 Message message = template.message(arguments, false); |
| 262 return new DiagnosticMessage(null, null, message); |
| 263 } |
| 264 } |
| 265 |
| 266 class FakeReporterOptions { |
| 267 bool get suppressHints => false; |
| 268 bool get hidePackageWarnings => false; |
| 269 } |
| 270 |
| 271 class _Loader { |
| 272 CompilerInput inputProvider; |
| 273 |
| 274 /// Maps dart-URIs to a known location in the sdk. |
| 275 Map<String, Uri> sdkLibraries; |
| 276 Map<Uri, SourceFile> _cache = {}; |
| 277 Packages packages; |
| 278 |
| 279 _Loader(this.inputProvider, this.sdkLibraries, this.packages); |
| 280 |
| 281 Future<SourceFile> loadFile(Uri uri) async { |
| 282 if (!uri.isAbsolute) throw 'Relative uri $uri provided to readScript.'; |
| 283 Uri resourceUri = _translateUri(uri); |
| 284 if (resourceUri == null || resourceUri.scheme == 'dart-ext') { |
| 285 throw '$uri not resolved or unsupported.'; |
| 286 } |
| 287 var file = _cache[resourceUri]; |
| 288 if (file != null) return _cache[resourceUri]; |
| 289 return _cache[resourceUri] = await _readFile(resourceUri); |
| 290 } |
| 291 |
| 292 Future<SourceFile> _readFile(Uri uri) async { |
| 293 var data = await inputProvider.readFromUri(uri); |
| 294 if (data is List<int>) return new Utf8BytesSourceFile(uri, data); |
| 295 if (data is String) return new StringSourceFile.fromUri(uri, data); |
| 296 // TODO(sigmund): properly handle errors, just report, return null, wrap |
| 297 // above and continue... |
| 298 throw "Expected a 'String' or a 'List<int>' from the input " |
| 299 "provider, but got: ${data.runtimeType}."; |
| 300 } |
| 301 |
| 302 Uri _translateUri(Uri uri) { |
| 303 if (uri.scheme == 'dart') return sdkLibraries[uri.path]; |
| 304 if (uri.scheme == 'package') return _translatePackageUri(uri); |
| 305 return uri; |
| 306 } |
| 307 |
| 308 Uri _translatePackageUri(Uri uri) { |
| 309 checkValidPackageUri(uri); |
| 310 return packages.resolve(uri, notFound: (_) { |
| 311 print('$uri not found'); |
| 312 }); |
| 313 } |
| 314 } |
| 315 |
| 316 class _ParserOptions implements ParserOptions { |
| 317 const _ParserOptions(); |
| 318 bool get enableGenericMethodSyntax => true; |
| 319 } |
OLD | NEW |