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 |