Chromium Code Reviews| 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:io' show exit; | |
|
Harry Terkelsen
2016/10/10 20:05:07
I remember we had some problems with exit and usin
Siggi Cherem (dart-lang)
2016/10/10 21:46:13
Seems to still be an issue, but in this case it's
| |
| 9 | |
| 10 import 'dart:async'; | |
| 11 import 'dart:io'; | |
|
Harry Terkelsen
2016/10/10 20:05:07
this makes the previous import redundant
Siggi Cherem (dart-lang)
2016/10/10 21:46:13
Done.
| |
| 12 | |
| 13 import 'package:compiler/src/diagnostics/messages.dart' | |
| 14 show Message, MessageTemplate; | |
| 15 import 'package:compiler/src/diagnostics/diagnostic_listener.dart'; | |
| 16 import 'package:compiler/src/io/source_file.dart'; | |
| 17 import 'package:compiler/src/options.dart'; | |
| 18 import 'package:compiler/src/platform_configuration.dart' as platform; | |
| 19 import 'package:compiler/src/source_file_provider.dart'; | |
| 20 import 'package:package_config/discovery.dart' show findPackages; | |
| 21 import 'package:package_config/packages.dart' show Packages; | |
| 22 import 'package:package_config/src/util.dart' show checkValidPackageUri; | |
| 23 | |
|
Harry Terkelsen
2016/10/10 20:05:07
sort these imports?
Siggi Cherem (dart-lang)
2016/10/10 21:46:12
Done.
| |
| 24 import 'package:compiler/compiler_new.dart'; | |
| 25 import 'package:compiler/src/common.dart'; | |
| 26 import 'package:compiler/src/options.dart' show ParserOptions; | |
| 27 import 'package:compiler/src/tokens/token.dart' show Token; | |
| 28 import 'package:compiler/src/parser/element_listener.dart' show ScannerOptions; | |
| 29 import 'package:compiler/src/parser/listener.dart'; | |
| 30 import 'package:compiler/src/parser/partial_parser.dart'; | |
| 31 import 'package:compiler/src/scanner/scanner.dart'; | |
| 32 import 'package:compiler/src/parser/node_listener.dart' show NodeListener; | |
| 33 import 'package:compiler/src/parser/parser.dart' show Parser; | |
| 34 | |
| 35 /// Cummulative total number of chars scanned. | |
|
Harry Terkelsen
2016/10/10 20:05:07
Cumulative
Siggi Cherem (dart-lang)
2016/10/10 21:46:12
Done.
| |
| 36 int scanTotalChars = 0; | |
| 37 | |
| 38 /// Cummulative time spent scanning. | |
|
Harry Terkelsen
2016/10/10 20:05:07
same
Siggi Cherem (dart-lang)
2016/10/10 21:46:12
Done.
| |
| 39 Stopwatch scanTimer = new Stopwatch(); | |
| 40 | |
| 41 /// Helper class used to load source files using dart2js's internal APIs. | |
| 42 _Loader loader; | |
| 43 | |
| 44 main(List<String> args) async { | |
| 45 // TODO(sigmund): provide sdk folder as well. | |
| 46 if (args.length < 2) { | |
| 47 print('usage: perf.dart <bench-id> <entry.dart>'); | |
| 48 exit(1); | |
| 49 } | |
| 50 var totalTimer = new Stopwatch()..start(); | |
| 51 | |
| 52 var bench = args[0]; | |
| 53 var entryUri = Uri.base.resolve(args[1]); | |
| 54 | |
| 55 await setup(entryUri); | |
| 56 | |
| 57 if (bench == 'scan') { | |
| 58 Set<SourceFile> files = await scanReachableFiles(entryUri); | |
| 59 // TODO(sigmund): consider replacing the warmup with instrumented snapshots. | |
| 60 for (int i = 0; i < 10; i++) scanFiles(files); | |
| 61 } else if (bench == 'parse') { | |
| 62 Set<SourceFile> files = await scanReachableFiles(entryUri); | |
| 63 // TODO(sigmund): consider replacing the warmup with instrumented snapshots. | |
| 64 for (int i = 0; i < 10; i++) parseFiles(files); | |
| 65 } else { | |
| 66 print('unsupported bench-id: $bench. Please specify "scan" or "parse"'); | |
| 67 // TODO(sigmund): implement the remaining benchmarks. | |
| 68 exit(1); | |
| 69 } | |
| 70 | |
| 71 totalTimer.stop(); | |
| 72 report("total", totalTimer.elapsedMicroseconds); | |
| 73 } | |
| 74 | |
| 75 Future setup(Uri entryUri) async { | |
| 76 var inputProvider = new CompilerSourceFileProvider(); | |
| 77 var sdkLibraries = await platform.load(_platformConfigUri, inputProvider); | |
| 78 var packages = await findPackages(entryUri); | |
| 79 loader = new _Loader(inputProvider, sdkLibraries, packages); | |
| 80 } | |
| 81 | |
| 82 /// Load and scans all files we need to process: files reachable from the | |
| 83 /// entrypoint and all core libraries automatically included by the VM. | |
| 84 Future<Set<SourceFile>> scanReachableFiles(Uri entryUri) async { | |
| 85 var files = new Set<SourceFile>(); | |
| 86 var loadTimer = new Stopwatch()..start(); | |
| 87 var entrypoints = [ | |
| 88 entryUri, | |
| 89 Uri.parse("dart:async"), | |
| 90 Uri.parse("dart:collection"), | |
| 91 Uri.parse("dart:convert"), | |
| 92 Uri.parse("dart:core"), | |
| 93 Uri.parse("dart:developer"), | |
| 94 Uri.parse("dart:_internal"), | |
| 95 Uri.parse("dart:io"), | |
| 96 Uri.parse("dart:isolate"), | |
| 97 Uri.parse("dart:math"), | |
| 98 Uri.parse("dart:mirrors"), | |
| 99 Uri.parse("dart:typed_data"), | |
| 100 ]; | |
| 101 for (var entry in entrypoints) { | |
| 102 await collectSources(await loader.loadFile(entry), files); | |
| 103 } | |
| 104 loadTimer.stop(); | |
| 105 | |
| 106 print('input size: ${scanTotalChars} chars'); | |
| 107 var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; | |
| 108 report("load", loadTime); | |
| 109 report("scan", scanTimer.elapsedMicroseconds); | |
| 110 return files; | |
| 111 } | |
| 112 | |
| 113 /// Scans every file in [files] and reports the time spent doing so. | |
| 114 void scanFiles(Set<SourceFile> files) { | |
| 115 scanTimer = new Stopwatch(); | |
| 116 var old = scanTotalChars; | |
| 117 scanTotalChars = 0; | |
| 118 for (var source in files) { | |
| 119 tokenize(source); | |
| 120 } | |
| 121 | |
| 122 // Report size and scanning time again. See discussion above. | |
|
Harry Terkelsen
2016/10/10 20:05:07
maybe "see discussion in parseFiles"?
Harry Terkelsen
2016/10/10 20:05:07
I don't see a comment discussing this?
Siggi Cherem (dart-lang)
2016/10/10 21:46:12
Acknowledged.
Siggi Cherem (dart-lang)
2016/10/10 21:46:12
good point - I added this later and the comment wa
| |
| 123 if (old != scanTotalChars) print('input size changed? ${old} chars'); | |
| 124 report("scan", scanTimer.elapsedMicroseconds); | |
| 125 } | |
| 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 | |
|
Harry Terkelsen
2016/10/10 20:05:07
move this comment to scanFiles?
Siggi Cherem (dart-lang)
2016/10/10 21:46:12
Done.
| |
| 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 var pTime = parseTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; | |
|
Harry Terkelsen
2016/10/10 20:05:07
pTime -> parseTime?
Siggi Cherem (dart-lang)
2016/10/10 21:46:12
Done.
| |
| 148 report("parse", pTime); | |
| 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 void 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 listener.popNode(); | |
|
Harry Terkelsen
2016/10/10 20:05:07
why are you calling this?
Siggi Cherem (dart-lang)
2016/10/10 21:46:12
Ah - I meant to return the value (even though I do
| |
| 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 Map _debug = {}; | |
|
Harry Terkelsen
2016/10/10 20:05:07
remove?
Siggi Cherem (dart-lang)
2016/10/10 21:46:12
Done.
| |
| 278 Packages packages; | |
| 279 | |
| 280 _Loader(this.inputProvider, this.sdkLibraries, this.packages); | |
| 281 | |
| 282 Future<SourceFile> loadFile(Uri uri) async { | |
| 283 if (!uri.isAbsolute) throw 'Relative uri $uri provided to readScript.'; | |
| 284 Uri resourceUri = _translateUri(uri); | |
| 285 if (resourceUri == null || resourceUri.scheme == 'dart-ext') { | |
| 286 throw '$uri not resolved or unsupported.'; | |
| 287 } | |
| 288 var file = _cache[resourceUri]; | |
|
Harry Terkelsen
2016/10/10 20:05:07
use putIfAbsent here
Siggi Cherem (dart-lang)
2016/10/10 21:46:13
Unfortunately I can't - the code that computes the
| |
| 289 if (file != null) return _cache[resourceUri]; | |
| 290 var r = _cache[resourceUri] = await _readFile(resourceUri); | |
| 291 _debug[resourceUri] = uri; | |
| 292 return r; | |
| 293 } | |
| 294 | |
| 295 Future<SourceFile> _readFile(Uri uri) async { | |
| 296 var data = await inputProvider.readFromUri(uri); | |
| 297 if (data is List<int>) return new Utf8BytesSourceFile(uri, data); | |
| 298 if (data is String) return new StringSourceFile.fromUri(uri, data); | |
| 299 // TODO(sigmund): properly handle errors, just report, return null, wrap | |
| 300 // above and continue... | |
| 301 throw "Expected a 'String' or a 'List<int>' from the input " | |
| 302 "provider, but got: ${data.runtimeType}."; | |
| 303 } | |
| 304 | |
| 305 Uri _translateUri(Uri uri) { | |
| 306 if (uri.scheme == 'dart') return sdkLibraries[uri.path]; | |
| 307 if (uri.scheme == 'package') return _translatePackageUri(uri); | |
| 308 return uri; | |
| 309 } | |
| 310 | |
| 311 Uri _translatePackageUri(Uri uri) { | |
| 312 checkValidPackageUri(uri); | |
| 313 return packages.resolve(uri, notFound: (_) { | |
| 314 print('$uri not found'); | |
| 315 }); | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 class _ParserOptions implements ParserOptions { | |
| 320 const _ParserOptions(); | |
| 321 bool get enableGenericMethodSyntax => true; | |
| 322 } | |
| OLD | NEW |