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 analyzer and measure its performance. | |
6 library analyzer_cli.tool.perf; | |
7 | |
8 import 'dart:io' show exit; | |
9 import 'package:analyzer/dart/ast/ast.dart'; | |
10 import 'package:analyzer/dart/ast/token.dart'; | |
11 import 'package:analyzer/file_system/file_system.dart' | |
12 show ResourceProvider, ResourceUriResolver | |
13 hide File; | |
14 import 'package:analyzer/file_system/physical_file_system.dart' | |
15 show PhysicalResourceProvider; | |
16 import 'package:analyzer/src/dart/scanner/reader.dart'; | |
17 import 'package:analyzer/src/dart/scanner/scanner.dart'; | |
18 import 'package:analyzer/src/dart/sdk/sdk.dart' show FolderBasedDartSdk; | |
19 import 'package:analyzer/src/error.dart'; | |
20 import 'package:analyzer/src/generated/error.dart'; | |
21 import 'package:analyzer/src/generated/java_io.dart' show JavaFile; | |
22 import 'package:analyzer/src/generated/parser.dart'; | |
23 import 'package:analyzer/src/generated/source.dart'; | |
24 import 'package:analyzer/src/generated/source_io.dart'; | |
25 | |
26 /// Cummulative total number of chars scanned. | |
27 int scanTotalChars = 0; | |
28 | |
29 /// Cummulative time spent scanning. | |
30 Stopwatch scanTimer = new Stopwatch(); | |
31 | |
32 /// Factory to load and resolve app, packages, and sdk sources. | |
33 SourceFactory sources; | |
34 | |
35 main(args) { | |
36 // TODO(sigmund): provide sdk folder as well. | |
37 if (args.length < 2) { | |
38 print('usage: perf.dart <bench-id> <package-root> <entry.dart>'); | |
39 exit(1); | |
40 } | |
41 var totalTimer = new Stopwatch()..start(); | |
42 | |
43 var bench = args[0]; | |
44 var packageRoot = args[1]; | |
45 var entryUri = Uri.base.resolve(args[2]); | |
46 | |
47 setup(packageRoot); | |
48 if (bench == 'scan') { | |
49 scanReachableFiles(entryUri); | |
50 } else if (bench == 'parse') { | |
51 Set<Source> files = scanReachableFiles(entryUri); | |
52 parseFiles(files); | |
53 } else { | |
54 print('unsupported bench-id: $bench. Please specify "scan" or "parse"'); | |
55 // TODO(sigmund): implement the remaining benchmarks. | |
56 exit(1); | |
57 } | |
58 | |
59 totalTimer.stop(); | |
60 report("Total", totalTimer.elapsedMicroseconds); | |
61 } | |
62 | |
63 /// Sets up analyzer to be able to load and resolve app, packages, and sdk | |
64 /// sources. | |
65 void setup(String packageRoot) { | |
66 var provider = PhysicalResourceProvider.INSTANCE; | |
67 sources = new SourceFactory([ | |
68 new ResourceUriResolver(provider), | |
69 new PackageUriResolver([new JavaFile(packageRoot)]), | |
70 new DartUriResolver( | |
71 new FolderBasedDartSdk(provider, provider.getFolder("sdk"))), | |
72 ]); | |
73 } | |
74 | |
75 /// Load and scans all files we need to process: files reachable from the | |
76 /// entrypoint and all core libraries automatically included by the VM. | |
77 Set<Source> scanReachableFiles(Uri entryUri) { | |
78 var files = new Set<Source>(); | |
79 var loadTimer = new Stopwatch()..start(); | |
80 collectSources(sources.forUri2(entryUri), files); | |
81 collectSources(sources.forUri("dart:async"), files); | |
82 collectSources(sources.forUri("dart:collection"), files); | |
83 collectSources(sources.forUri("dart:convert"), files); | |
84 collectSources(sources.forUri("dart:core"), files); | |
85 collectSources(sources.forUri("dart:developer"), files); | |
86 collectSources(sources.forUri("dart:_internal"), files); | |
87 collectSources(sources.forUri("dart:isolate"), files); | |
88 collectSources(sources.forUri("dart:math"), files); | |
89 collectSources(sources.forUri("dart:mirrors"), files); | |
90 collectSources(sources.forUri("dart:typed_data"), files); | |
91 loadTimer.stop(); | |
92 | |
93 print('input size: ${scanTotalChars} chars'); | |
94 var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; | |
95 report("Loader", loadTime); | |
96 report("Scanner", scanTimer.elapsedMicroseconds); | |
97 return files; | |
98 } | |
99 | |
100 /// Parses every file in [files] and reports the time spent doing so. | |
101 void parseFiles(Set<Source> files) { | |
102 // The code below will record again how many chars are scanned and how long it | |
103 // takes to scan them, even though we already did so in [scanReachableFiles]. | |
104 // Recording and reporting this twice is unnecessary, but we do so for now to | |
105 // validate that the results are consistent. | |
106 scanTimer = new Stopwatch(); | |
107 var oldTotal = scanTotalChars; | |
108 scanTotalChars = 0; | |
109 var parseTimer = new Stopwatch()..start(); | |
110 for (var source in files) { | |
111 parseFull(source); | |
112 } | |
113 parseTimer.stop(); | |
114 | |
115 // Report size and scanning time again. See discussion above. | |
116 if (oldTotal != scanTotalChars) print('input size changed? ${total} chars'); | |
117 report("Scanner", scanTimer.elapsedMicroseconds); | |
118 | |
119 var pTime = parseTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; | |
120 report("Parser", pTime); | |
121 } | |
122 | |
123 /// Add to [files] all sources reachable from [start]. | |
124 void collectSources(Source start, Set<Source> files) { | |
125 if (!files.add(start)) return; | |
126 var unit = parseDirectives(start); | |
127 for (var directive in unit.directives) { | |
128 if (directive is! UriBasedDirective) continue; | |
129 var next = sources.resolveUri(start, directive.uri.stringValue); | |
Brian Wilkerson
2016/09/16 15:32:02
This introduces a compilation error:
ERROR: The g
| |
130 collectSources(next, files); | |
131 } | |
132 } | |
133 | |
134 /// Uses the diet-parser to parse only directives in [source]. | |
135 CompilationUnit parseDirectives(Source source) { | |
136 var token = tokenize(source); | |
137 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); | |
138 return parser.parseDirectives(token); | |
139 } | |
140 | |
141 /// Parse the full body of [source] and return it's compilation unit. | |
142 CompilationUnit parseFull(Source source) { | |
143 var token = tokenize(source); | |
144 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); | |
145 return parser.parseCompilationUnit(token); | |
146 } | |
147 | |
148 /// Scan [source] and return the first token produced by the scanner. | |
149 Token tokenize(Source source) { | |
150 scanTimer.start(); | |
151 var contents = source.contents.data; | |
152 scanTotalChars += contents.length; | |
153 // TODO(sigmund): is there a way to scan from a random-access-file without | |
154 // first converting to String? | |
155 var scanner = new Scanner(source, new CharSequenceReader(contents), | |
156 AnalysisErrorListener.NULL_LISTENER); | |
157 var token = scanner.tokenize(); | |
158 scanTimer.stop(); | |
159 return token; | |
160 } | |
161 | |
162 /// Report that metric [name] took [time] micro-seconds to process | |
163 /// [scanTotalChars] characters. | |
164 void report(String name, int time) { | |
165 var sb = new StringBuffer(); | |
166 sb.write('$name: ${time ~/ 1000} ms'); | |
167 sb.write(', ${scanTotalChars * 1000 ~/ time} chars/ms'); | |
168 print('$sb'); | |
169 } | |
OLD | NEW |