Chromium Code Reviews| 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 front_end and measure its performance. | 5 /// An entrypoint used to run portions of front_end and measure its performance. |
| 6 library front_end.tool.perf; | 6 library front_end.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 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 30 import 'package:front_end/src/scanner/token.dart'; | 30 import 'package:front_end/src/scanner/token.dart'; |
| 31 import 'package:kernel/kernel.dart'; | 31 import 'package:kernel/kernel.dart'; |
| 32 import 'package:package_config/discovery.dart'; | 32 import 'package:package_config/discovery.dart'; |
| 33 | 33 |
| 34 main(List<String> args) async { | 34 main(List<String> args) async { |
| 35 // TODO(sigmund): provide sdk folder as well. | 35 // TODO(sigmund): provide sdk folder as well. |
| 36 if (args.length < 2) { | 36 if (args.length < 2) { |
| 37 print('usage: perf.dart <bench-id> <entry.dart>'); | 37 print('usage: perf.dart <bench-id> <entry.dart>'); |
| 38 exit(1); | 38 exit(1); |
| 39 } | 39 } |
| 40 var totalTimer = new Stopwatch()..start(); | |
| 41 | 40 |
| 42 var bench = args[0]; | 41 var bench = args[0]; |
| 43 var entryUri = Uri.base.resolve(args[1]); | 42 var entryUri = Uri.base.resolve(args[1]); |
| 44 | 43 |
| 45 await setup(entryUri); | 44 await setup(entryUri); |
| 46 | 45 |
| 46 Set<Source> files = scanReachableFiles(entryUri); | |
| 47 var handlers = { | 47 var handlers = { |
| 48 'scan': () async { | 48 'scan': () async => scanFiles(files), |
| 49 Set<Source> files = scanReachableFiles(entryUri); | 49 'parse': () async => parseFiles(files), |
| 50 // TODO(sigmund): replace the warmup with instrumented snapshots. | |
| 51 for (int i = 0; i < 10; i++) scanFiles(files); | |
| 52 }, | |
| 53 'parse': () async { | |
| 54 Set<Source> files = scanReachableFiles(entryUri); | |
| 55 // TODO(sigmund): replace the warmup with instrumented snapshots. | |
| 56 for (int i = 0; i < 10; i++) parseFiles(files); | |
| 57 }, | |
| 58 'kernel_gen_e2e': () async { | 50 'kernel_gen_e2e': () async { |
| 59 // TODO(sigmund): remove. This is used to compute the input size, we | 51 await generateKernel(entryUri, useSdkSummary: false); |
| 60 // should extract input size from frontend instead. | |
| 61 scanReachableFiles(entryUri); | |
| 62 // TODO(sigmund): replace this warmup. Note that for very large programs, | |
| 63 // the GC pressure on the VM seems to make this worse with time (maybe we | |
| 64 // are leaking memory?). That's why we run it twice and not 10 times. | |
| 65 for (int i = 0; i < 2; i++) { | |
| 66 await generateKernel(entryUri, useSdkSummary: false); | |
| 67 } | |
| 68 }, | 52 }, |
| 69 'kernel_gen_e2e_sum': () async { | 53 'kernel_gen_e2e_sum': () async { |
| 70 // TODO(sigmund): remove. This is incorrect since it includes sizes for | 54 await generateKernel(entryUri, useSdkSummary: true, compileSdk: false); |
| 71 // files that will not be loaded when using summaries. We need to extract | |
| 72 // input size from frontend instead. | |
| 73 scanReachableFiles(entryUri); | |
| 74 // TODO(sigmund): replace this warmup. Note that for very large programs, | |
| 75 // the GC pressure on the VM seems to make this worse with time (maybe we | |
| 76 // are leaking memory?). That's why we run it twice and not 10 times. | |
| 77 for (int i = 0; i < 2; i++) { | |
| 78 await generateKernel(entryUri, useSdkSummary: true, compileSdk: false); | |
| 79 } | |
| 80 }, | 55 }, |
| 81 'unlinked_summarize': () async { | 56 'unlinked_summarize': () async => summarize(files), |
| 82 Set<Source> files = scanReachableFiles(entryUri); | 57 'prelinked_summarize': () async => summarize(files, prelink: true), |
| 83 // TODO(sigmund): replace the warmup with instrumented snapshots. | 58 'linked_summarize': () async => summarize(files, link: true), |
| 84 for (int i = 0; i < 10; i++) unlinkedSummarizeFiles(files); | |
| 85 }, | |
| 86 'prelinked_summarize': () async { | |
| 87 Set<Source> files = scanReachableFiles(entryUri); | |
| 88 // TODO(sigmund): replace the warmup with instrumented snapshots. | |
| 89 for (int i = 0; i < 10; i++) prelinkedSummarizeFiles(files); | |
| 90 }, | |
| 91 'linked_summarize': () async { | |
| 92 Set<Source> files = scanReachableFiles(entryUri); | |
| 93 // TODO(sigmund): replace the warmup with instrumented snapshots. | |
| 94 for (int i = 0; i < 10; i++) linkedSummarizeFiles(files); | |
| 95 } | |
| 96 }; | 59 }; |
| 97 | 60 |
| 98 var handler = handlers[bench]; | 61 var handler = handlers[bench]; |
| 99 if (handler == null) { | 62 if (handler == null) { |
| 100 // TODO(sigmund): implement the remaining benchmarks. | |
| 101 print('unsupported bench-id: $bench. Please specify one of the following: ' | 63 print('unsupported bench-id: $bench. Please specify one of the following: ' |
| 102 '${handlers.keys.join(", ")}'); | 64 '${handlers.keys.join(", ")}'); |
| 103 exit(1); | 65 exit(1); |
| 104 } | 66 } |
| 105 await handler(); | |
| 106 | 67 |
| 107 totalTimer.stop(); | 68 // TODO(sigmund): replace the warmup with instrumented snapshots. |
| 108 report("total", totalTimer.elapsedMicroseconds); | 69 int iterations = bench.contains('kernel_gen') ? 2 : 10; |
| 70 for (int i = 0; i < iterations; i++) { | |
| 71 var totalTimer = new Stopwatch()..start(); | |
| 72 print('== iteration $i'); | |
| 73 await handler(); | |
| 74 totalTimer.stop(); | |
| 75 report("total", totalTimer.elapsedMicroseconds); | |
| 76 } | |
| 77 | |
| 109 } | 78 } |
| 110 | 79 |
| 111 /// Cumulative time spent parsing. | 80 /// Cumulative time spent parsing. |
| 112 Stopwatch parseTimer = new Stopwatch(); | 81 Stopwatch parseTimer = new Stopwatch(); |
| 113 | 82 |
| 114 /// Cumulative time spent prelinking summaries. | 83 /// Cumulative time spent building unlinked summaries. |
| 115 Stopwatch prelinkSummaryTimer = new Stopwatch(); | 84 Stopwatch unlinkedSummarizeTimer = new Stopwatch(); |
| 116 | 85 |
| 117 /// Cumulative time spent scanning. | 86 /// Cumulative time spent scanning. |
| 118 Stopwatch scanTimer = new Stopwatch(); | 87 Stopwatch scanTimer = new Stopwatch(); |
| 119 | 88 |
| 120 /// Cumulative total number of chars scanned. | 89 /// Size of all sources. |
| 121 int scanTotalChars = 0; | 90 int inputSize = 0; |
| 122 | 91 |
| 123 /// Factory to load and resolve app, packages, and sdk sources. | 92 /// Factory to load and resolve app, packages, and sdk sources. |
| 124 SourceFactory sources; | 93 SourceFactory sources; |
| 125 | 94 |
| 126 /// Cumulative time spent building unlinked summaries. | |
| 127 Stopwatch unlinkedSummarizeTimer = new Stopwatch(); | |
| 128 | |
| 129 /// Add to [files] all sources reachable from [start]. | 95 /// Add to [files] all sources reachable from [start]. |
| 130 void collectSources(Source start, Set<Source> files) { | 96 void collectSources(Source start, Set<Source> files) { |
| 131 if (!files.add(start)) return; | 97 if (!files.add(start)) return; |
| 132 var unit = parseDirectives(start); | 98 var unit = parseDirectives(start); |
| 133 for (var directive in unit.directives) { | 99 for (var directive in unit.directives) { |
| 134 if (directive is UriBasedDirective) { | 100 if (directive is UriBasedDirective) { |
| 135 var next = sources.resolveUri(start, directive.uri.stringValue); | 101 var next = sources.resolveUri(start, directive.uri.stringValue); |
| 136 collectSources(next, files); | 102 collectSources(next, files); |
| 137 } | 103 } |
| 138 } | 104 } |
| 139 } | 105 } |
| 140 | 106 |
| 141 Future<Program> generateKernel(Uri entryUri, | 107 Future<Program> generateKernel(Uri entryUri, |
| 142 {bool useSdkSummary: false, bool compileSdk: true}) async { | 108 {bool useSdkSummary: false, bool compileSdk: true}) async { |
| 109 // TODO(sigmund): this is here only to compute the input size, | |
| 110 // we should extract the input size from the frontend instead. | |
| 111 scanReachableFiles(entryUri); | |
| 112 | |
| 143 var dartkTimer = new Stopwatch()..start(); | 113 var dartkTimer = new Stopwatch()..start(); |
| 144 // TODO(sigmund): add a constructor with named args to compiler options. | 114 // TODO(sigmund): add a constructor with named args to compiler options. |
| 145 var options = new CompilerOptions() | 115 var options = new CompilerOptions() |
| 146 ..strongMode = false | 116 ..strongMode = false |
| 147 ..compileSdk = compileSdk | 117 ..compileSdk = compileSdk |
| 148 ..packagesFilePath = '.packages' | 118 ..packagesFilePath = '.packages' |
| 149 ..onError = ((e) => print('${e.message}')); | 119 ..onError = ((e) => print('${e.message}')); |
| 150 if (useSdkSummary) { | 120 if (useSdkSummary) { |
| 151 // TODO(sigmund): adjust path based on the benchmark runner architecture. | 121 // TODO(sigmund): adjust path based on the benchmark runner architecture. |
| 152 // Possibly let the runner make the file available at an architecture | 122 // Possibly let the runner make the file available at an architecture |
| 153 // independent location. | 123 // independent location. |
| 154 options.sdkSummary = 'out/ReleaseX64/dart-sdk/lib/_internal/spec.sum'; | 124 options.sdkSummary = 'out/ReleaseX64/dart-sdk/lib/_internal/spec.sum'; |
| 155 } else { | 125 } else { |
| 156 options.sdkPath = 'sdk'; | 126 options.sdkPath = 'sdk'; |
| 157 } | 127 } |
| 158 Program program = await kernelForProgram(entryUri, options); | 128 Program program = await kernelForProgram(entryUri, options); |
| 159 dartkTimer.stop(); | 129 dartkTimer.stop(); |
| 160 var suffix = useSdkSummary ? "_sum" : ""; | 130 var suffix = useSdkSummary ? "_sum" : ""; |
| 161 report("kernel_gen_e2e${suffix}", dartkTimer.elapsedMicroseconds); | 131 report("kernel_gen_e2e${suffix}", dartkTimer.elapsedMicroseconds); |
| 162 return program; | 132 return program; |
| 163 } | 133 } |
| 164 | 134 |
| 165 /// Generates unlinkmed summaries for all files in [files], and returns them in | 135 /// Generates unlinkmed summaries for all files in [files], and returns them in |
| 166 /// an [UnlinkedSummaries] container. | 136 /// an [_UnlinkedSummaries] container. |
| 167 UnlinkedSummaries generateUnlinkedSummaries(Set<Source> files) { | 137 _UnlinkedSummaries generateUnlinkedSummaries(Set<Source> files) { |
| 168 var unlinkedSummaries = new UnlinkedSummaries(); | 138 var unlinkedSummaries = new _UnlinkedSummaries(); |
| 169 for (var source in files) { | 139 for (var source in files) { |
| 170 unlinkedSummaries.summariesByUri[source.uri.toString()] = | 140 unlinkedSummaries.summariesByUri[source.uri.toString()] = |
| 171 unlinkedSummarize(source); | 141 unlinkedSummarize(source); |
| 172 } | 142 } |
| 173 return unlinkedSummaries; | 143 return unlinkedSummaries; |
| 174 } | 144 } |
| 175 | 145 |
| 176 /// Produces linked summaries for every file in [files] and reports the time | 146 /// Produces linked summaries for every file in [files] and reports the time |
|
Paul Berry
2017/01/03 17:58:34
Doc comment is no longer accurate.
Siggi Cherem (dart-lang)
2017/01/04 00:01:04
Done.
| |
| 177 /// spent doing so. | 147 /// spent doing so. |
| 178 void linkedSummarizeFiles(Set<Source> files) { | 148 void summarize(Set<Source> files, {bool prelink: false, bool link: false}) { |
| 179 // The code below will record again how many chars are scanned and how long it | |
| 180 // takes to scan them, even though we already did so in [scanReachableFiles]. | |
| 181 // Recording and reporting this twice is unnecessary, but we do so for now to | |
| 182 // validate that the results are consistent. | |
| 183 scanTimer = new Stopwatch(); | 149 scanTimer = new Stopwatch(); |
| 184 var old = scanTotalChars; | |
| 185 scanTotalChars = 0; | |
| 186 parseTimer = new Stopwatch(); | 150 parseTimer = new Stopwatch(); |
| 187 unlinkedSummarizeTimer = new Stopwatch(); | 151 unlinkedSummarizeTimer = new Stopwatch(); |
| 188 var unlinkedSummaries = generateUnlinkedSummaries(files); | 152 var unlinkedSummaries = generateUnlinkedSummaries(files); |
| 189 prelinkSummaryTimer = new Stopwatch(); | |
| 190 Map<String, LinkedLibraryBuilder> prelinkedLibraries = | |
| 191 prelinkSummaries(files, unlinkedSummaries); | |
| 192 var linkTimer = new Stopwatch()..start(); | |
| 193 LinkedLibrary getDependency(String uri) { | |
| 194 // getDependency should never be called because all dependencies are present | |
| 195 // in [prelinkedLibraries]. | |
| 196 print('Warning: getDependency called for: $uri'); | |
| 197 return null; | |
| 198 } | |
| 199 | |
| 200 bool strong = true; | |
| 201 relink(prelinkedLibraries, getDependency, unlinkedSummaries.getUnit, strong); | |
| 202 linkTimer.stop(); | |
| 203 | |
| 204 if (old != scanTotalChars) print('input size changed? ${old} chars'); | |
| 205 report("scan", scanTimer.elapsedMicroseconds); | 153 report("scan", scanTimer.elapsedMicroseconds); |
| 206 report("parse", parseTimer.elapsedMicroseconds); | 154 report("parse", parseTimer.elapsedMicroseconds); |
| 207 report('unlinked summarize', unlinkedSummarizeTimer.elapsedMicroseconds); | 155 report('unlink extract', unlinkedSummarizeTimer.elapsedMicroseconds); |
|
Paul Berry
2017/01/03 17:58:34
I don't really have strong feelings about nomencla
Siggi Cherem (dart-lang)
2017/01/04 00:01:04
In all honestly, I'm not convinced about "extract"
Paul Berry
2017/01/04 13:55:43
In that case I'd prefer (b), on the theory that tr
| |
| 208 report( | 156 report( |
| 209 'unlinked summarize + parse', | 157 'unlinked_summarize', |
| 210 unlinkedSummarizeTimer.elapsedMicroseconds + | 158 unlinkedSummarizeTimer.elapsedMicroseconds + |
| 211 parseTimer.elapsedMicroseconds); | 159 parseTimer.elapsedMicroseconds); |
| 212 report('prelink', prelinkSummaryTimer.elapsedMicroseconds); | 160 |
| 213 report('link', linkTimer.elapsedMicroseconds); | 161 if (prelink || link) { |
| 162 var prelinkTimer = new Stopwatch()..start(); | |
| 163 var prelinkedLibraries = prelinkSummaries(files, unlinkedSummaries); | |
| 164 prelinkTimer.stop(); | |
| 165 report('prelinked_summarize', prelinkTimer.elapsedMicroseconds); | |
| 166 | |
| 167 if (link) { | |
| 168 var linkTimer = new Stopwatch()..start(); | |
| 169 LinkedLibrary getDependency(String uri) { | |
| 170 // getDependency should never be called because all dependencies are | |
| 171 // present in [prelinkedLibraries]. | |
| 172 print('Warning: getDependency called for: $uri'); | |
| 173 return null; | |
| 174 } | |
| 175 | |
| 176 relink(prelinkedLibraries, getDependency, unlinkedSummaries.getUnit, | |
| 177 true /*strong*/); | |
| 178 linkTimer.stop(); | |
| 179 report('linked_summarize', linkTimer.elapsedMicroseconds); | |
| 180 } | |
| 181 } | |
| 214 } | 182 } |
| 215 | 183 |
| 216 /// Uses the diet-parser to parse only directives in [source]. | 184 /// Uses the diet-parser to parse only directives in [source]. |
| 217 CompilationUnit parseDirectives(Source source) { | 185 CompilationUnit parseDirectives(Source source) { |
| 218 var token = tokenize(source); | 186 var token = tokenize(source); |
| 219 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); | 187 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); |
| 220 return parser.parseDirectives(token); | 188 return parser.parseDirectives(token); |
| 221 } | 189 } |
| 222 | 190 |
| 223 /// Parses every file in [files] and reports the time spent doing so. | 191 /// Parses every file in [files] and reports the time spent doing so. |
| 224 void parseFiles(Set<Source> files) { | 192 void parseFiles(Set<Source> files) { |
| 225 // The code below will record again how many chars are scanned and how long it | |
| 226 // takes to scan them, even though we already did so in [scanReachableFiles]. | |
| 227 // Recording and reporting this twice is unnecessary, but we do so for now to | |
| 228 // validate that the results are consistent. | |
| 229 scanTimer = new Stopwatch(); | 193 scanTimer = new Stopwatch(); |
| 230 var old = scanTotalChars; | |
| 231 scanTotalChars = 0; | |
| 232 parseTimer = new Stopwatch(); | 194 parseTimer = new Stopwatch(); |
| 233 for (var source in files) { | 195 for (var source in files) { |
| 234 parseFull(source); | 196 parseFull(source); |
| 235 } | 197 } |
| 236 | 198 |
| 237 // Report size and scanning time again. See discussion above. | |
| 238 if (old != scanTotalChars) print('input size changed? ${old} chars'); | |
| 239 report("scan", scanTimer.elapsedMicroseconds); | 199 report("scan", scanTimer.elapsedMicroseconds); |
| 240 report("parse", parseTimer.elapsedMicroseconds); | 200 report("parse", parseTimer.elapsedMicroseconds); |
| 241 } | 201 } |
| 242 | 202 |
| 243 /// Parse the full body of [source] and return it's compilation unit. | 203 /// Parse the full body of [source] and return it's compilation unit. |
| 244 CompilationUnit parseFull(Source source) { | 204 CompilationUnit parseFull(Source source) { |
| 245 var token = tokenize(source); | 205 var token = tokenize(source); |
| 246 parseTimer.start(); | 206 parseTimer.start(); |
| 247 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); | 207 var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); |
| 248 var unit = parser.parseCompilationUnit(token); | 208 var unit = parser.parseCompilationUnit(token); |
| 249 parseTimer.stop(); | 209 parseTimer.stop(); |
| 250 return unit; | 210 return unit; |
| 251 } | 211 } |
| 252 | 212 |
| 253 /// Produces prelinked summaries for every file in [files] and reports the time | |
| 254 /// spent doing so. | |
| 255 void prelinkedSummarizeFiles(Set<Source> files) { | |
| 256 // The code below will record again how many chars are scanned and how long it | |
| 257 // takes to scan them, even though we already did so in [scanReachableFiles]. | |
| 258 // Recording and reporting this twice is unnecessary, but we do so for now to | |
| 259 // validate that the results are consistent. | |
| 260 scanTimer = new Stopwatch(); | |
| 261 var old = scanTotalChars; | |
| 262 scanTotalChars = 0; | |
| 263 parseTimer = new Stopwatch(); | |
| 264 unlinkedSummarizeTimer = new Stopwatch(); | |
| 265 var unlinkedSummaries = generateUnlinkedSummaries(files); | |
| 266 prelinkSummaryTimer = new Stopwatch(); | |
| 267 prelinkSummaries(files, unlinkedSummaries); | |
| 268 | |
| 269 if (old != scanTotalChars) print('input size changed? ${old} chars'); | |
| 270 report("scan", scanTimer.elapsedMicroseconds); | |
| 271 report("parse", parseTimer.elapsedMicroseconds); | |
| 272 report('unlinked summarize', unlinkedSummarizeTimer.elapsedMicroseconds); | |
| 273 report( | |
| 274 'unlinked summarize + parse', | |
| 275 unlinkedSummarizeTimer.elapsedMicroseconds + | |
| 276 parseTimer.elapsedMicroseconds); | |
| 277 report('prelink', prelinkSummaryTimer.elapsedMicroseconds); | |
| 278 } | |
| 279 | |
| 280 /// Prelinks all the summaries for [files], using [unlinkedSummaries] to obtain | 213 /// Prelinks all the summaries for [files], using [unlinkedSummaries] to obtain |
| 281 /// their unlinked summaries. | 214 /// their unlinked summaries. |
| 282 /// | 215 /// |
| 283 /// The return value is suitable for passing to the summary linker. | 216 /// The return value is suitable for passing to the summary linker. |
| 284 Map<String, LinkedLibraryBuilder> prelinkSummaries( | 217 Map<String, LinkedLibraryBuilder> prelinkSummaries( |
| 285 Set<Source> files, UnlinkedSummaries unlinkedSummaries) { | 218 Set<Source> files, _UnlinkedSummaries unlinkedSummaries) { |
| 286 prelinkSummaryTimer.start(); | 219 Set<String> libraryUris = files.map((source) => '${source.uri}').toSet(); |
| 287 Set<String> libraryUris = | |
| 288 files.map((source) => source.uri.toString()).toSet(); | |
| 289 | 220 |
| 290 String getDeclaredVariable(String s) => null; | 221 String getDeclaredVariable(String s) => null; |
| 291 var prelinkedLibraries = | 222 var prelinkedLibraries = |
| 292 setupForLink(libraryUris, unlinkedSummaries.getUnit, getDeclaredVariable); | 223 setupForLink(libraryUris, unlinkedSummaries.getUnit, getDeclaredVariable); |
| 293 prelinkSummaryTimer.stop(); | |
| 294 return prelinkedLibraries; | 224 return prelinkedLibraries; |
| 295 } | 225 } |
| 296 | 226 |
| 297 /// Report that metric [name] took [time] micro-seconds to process | 227 /// Report that metric [name] took [time] micro-seconds to process |
| 298 /// [scanTotalChars] characters. | 228 /// [inputSize] characters. |
| 299 void report(String name, int time) { | 229 void report(String name, int time) { |
| 300 var sb = new StringBuffer(); | 230 var sb = new StringBuffer(); |
| 301 sb.write('$name: $time us, ${time ~/ 1000} ms'); | 231 var padding = " " * (20 - name.length); |
| 302 sb.write(', ${scanTotalChars * 1000 ~/ time} chars/ms'); | 232 sb.write('$name:$padding $time us, ${time ~/ 1000} ms'); |
| 233 sb.write(', ${time * 1000 ~/ inputSize} ns/char'); | |
| 303 print('$sb'); | 234 print('$sb'); |
| 304 } | 235 } |
| 305 | 236 |
| 306 /// Scans every file in [files] and reports the time spent doing so. | 237 /// Scans every file in [files] and reports the time spent doing so. |
| 307 void scanFiles(Set<Source> files) { | 238 void scanFiles(Set<Source> files) { |
| 308 // The code below will record again how many chars are scanned and how long it | 239 // `tokenize` records how many chars are scanned and how long it takes to scan |
| 309 // takes to scan them, even though we already did so in [scanReachableFiles]. | 240 // them. As this function is called repeatedly when running as a benchmark, we |
| 310 // Recording and reporting this twice is unnecessary, but we do so for now to | 241 // make sure to clear the data and compute it again every time. |
| 311 // validate that the results are consistent. | |
| 312 scanTimer = new Stopwatch(); | 242 scanTimer = new Stopwatch(); |
| 313 var old = scanTotalChars; | |
| 314 scanTotalChars = 0; | |
| 315 for (var source in files) { | 243 for (var source in files) { |
| 316 tokenize(source); | 244 tokenize(source); |
| 317 } | 245 } |
| 318 | 246 |
| 319 // Report size and scanning time again. See discussion above. | |
| 320 if (old != scanTotalChars) print('input size changed? ${old} chars'); | |
| 321 report("scan", scanTimer.elapsedMicroseconds); | 247 report("scan", scanTimer.elapsedMicroseconds); |
| 322 } | 248 } |
| 323 | 249 |
| 324 /// Load and scans all files we need to process: files reachable from the | 250 /// Load and scans all files we need to process: files reachable from the |
| 325 /// entrypoint and all core libraries automatically included by the VM. | 251 /// entrypoint and all core libraries automatically included by the VM. |
| 326 Set<Source> scanReachableFiles(Uri entryUri) { | 252 Set<Source> scanReachableFiles(Uri entryUri) { |
| 327 var files = new Set<Source>(); | 253 var files = new Set<Source>(); |
| 328 var loadTimer = new Stopwatch()..start(); | 254 var loadTimer = new Stopwatch()..start(); |
| 329 collectSources(sources.forUri2(entryUri), files); | 255 collectSources(sources.forUri2(entryUri), files); |
| 330 | 256 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 341 "dart:typed_data", | 267 "dart:typed_data", |
| 342 "dart:io" | 268 "dart:io" |
| 343 ]; | 269 ]; |
| 344 | 270 |
| 345 for (var lib in libs) { | 271 for (var lib in libs) { |
| 346 collectSources(sources.forUri(lib), files); | 272 collectSources(sources.forUri(lib), files); |
| 347 } | 273 } |
| 348 | 274 |
| 349 loadTimer.stop(); | 275 loadTimer.stop(); |
| 350 | 276 |
| 351 print('input size: ${scanTotalChars} chars'); | 277 for (var s in files) inputSize += s.contents.data.length; |
| 278 print('input size: ${inputSize} chars'); | |
| 352 var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; | 279 var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; |
| 353 report("load", loadTime); | 280 report("load", loadTime); |
| 354 report("scan", scanTimer.elapsedMicroseconds); | 281 report("scan", scanTimer.elapsedMicroseconds); |
| 355 return files; | 282 return files; |
| 356 } | 283 } |
| 357 | 284 |
| 358 /// Sets up analyzer to be able to load and resolve app, packages, and sdk | 285 /// Sets up analyzer to be able to load and resolve app, packages, and sdk |
| 359 /// sources. | 286 /// sources. |
| 360 Future setup(Uri entryUri) async { | 287 Future setup(Uri entryUri) async { |
| 361 var provider = PhysicalResourceProvider.INSTANCE; | 288 var provider = PhysicalResourceProvider.INSTANCE; |
| 362 var packageMap = new ContextBuilder(provider, null, null) | 289 var packageMap = new ContextBuilder(provider, null, null) |
| 363 .convertPackagesToMap(await findPackages(entryUri)); | 290 .convertPackagesToMap(await findPackages(entryUri)); |
| 364 sources = new SourceFactory([ | 291 sources = new SourceFactory([ |
| 365 new ResourceUriResolver(provider), | 292 new ResourceUriResolver(provider), |
| 366 new PackageMapUriResolver(provider, packageMap), | 293 new PackageMapUriResolver(provider, packageMap), |
| 367 new DartUriResolver( | 294 new DartUriResolver( |
| 368 new FolderBasedDartSdk(provider, provider.getFolder("sdk"))), | 295 new FolderBasedDartSdk(provider, provider.getFolder("sdk"))), |
| 369 ]); | 296 ]); |
| 370 } | 297 } |
| 371 | 298 |
| 372 /// Scan [source] and return the first token produced by the scanner. | 299 /// Scan [source] and return the first token produced by the scanner. |
| 373 Token tokenize(Source source) { | 300 Token tokenize(Source source) { |
| 374 scanTimer.start(); | 301 scanTimer.start(); |
| 375 var contents = source.contents.data; | |
| 376 scanTotalChars += contents.length; | |
| 377 // TODO(sigmund): is there a way to scan from a random-access-file without | 302 // TODO(sigmund): is there a way to scan from a random-access-file without |
| 378 // first converting to String? | 303 // first converting to String? |
| 379 var scanner = new _Scanner(contents); | 304 var scanner = new _Scanner(source.contents.data); |
| 380 var token = scanner.tokenize(); | 305 var token = scanner.tokenize(); |
| 381 scanTimer.stop(); | 306 scanTimer.stop(); |
| 382 return token; | 307 return token; |
| 383 } | 308 } |
| 384 | 309 |
| 385 UnlinkedUnitBuilder unlinkedSummarize(Source source) { | 310 UnlinkedUnitBuilder unlinkedSummarize(Source source) { |
| 386 var unit = parseFull(source); | 311 var unit = parseFull(source); |
| 387 unlinkedSummarizeTimer.start(); | 312 unlinkedSummarizeTimer.start(); |
| 388 var unlinkedUnit = serializeAstUnlinked(unit); | 313 var unlinkedUnit = serializeAstUnlinked(unit); |
| 389 unlinkedSummarizeTimer.stop(); | 314 unlinkedSummarizeTimer.stop(); |
| 390 return unlinkedUnit; | 315 return unlinkedUnit; |
| 391 } | 316 } |
| 392 | 317 |
| 393 /// Produces unlinked summaries for every file in [files] and reports the time | |
| 394 /// spent doing so. | |
| 395 void unlinkedSummarizeFiles(Set<Source> files) { | |
| 396 // The code below will record again how many chars are scanned and how long it | |
| 397 // takes to scan them, even though we already did so in [scanReachableFiles]. | |
| 398 // Recording and reporting this twice is unnecessary, but we do so for now to | |
| 399 // validate that the results are consistent. | |
| 400 scanTimer = new Stopwatch(); | |
| 401 var old = scanTotalChars; | |
| 402 scanTotalChars = 0; | |
| 403 parseTimer = new Stopwatch(); | |
| 404 unlinkedSummarizeTimer = new Stopwatch(); | |
| 405 generateUnlinkedSummaries(files); | |
| 406 | |
| 407 if (old != scanTotalChars) print('input size changed? ${old} chars'); | |
| 408 report("scan", scanTimer.elapsedMicroseconds); | |
| 409 report("parse", parseTimer.elapsedMicroseconds); | |
| 410 report('unlinked summarize', unlinkedSummarizeTimer.elapsedMicroseconds); | |
| 411 report( | |
| 412 'unlinked summarize + parse', | |
| 413 unlinkedSummarizeTimer.elapsedMicroseconds + | |
| 414 parseTimer.elapsedMicroseconds); | |
| 415 } | |
| 416 | |
| 417 /// Simple container for a mapping from URI string to an unlinked summary. | 318 /// Simple container for a mapping from URI string to an unlinked summary. |
| 418 class UnlinkedSummaries { | 319 class _UnlinkedSummaries { |
| 419 final summariesByUri = <String, UnlinkedUnit>{}; | 320 final summariesByUri = <String, UnlinkedUnit>{}; |
| 420 | 321 |
| 421 /// Get the unlinked summary for the given URI, and report a warning if it | 322 /// Get the unlinked summary for the given URI, and report a warning if it |
| 422 /// can't be found. | 323 /// can't be found. |
| 423 UnlinkedUnit getUnit(String uri) { | 324 UnlinkedUnit getUnit(String uri) { |
| 424 var result = summariesByUri[uri]; | 325 var result = summariesByUri[uri]; |
| 425 if (result == null) { | 326 if (result == null) { |
| 426 print('Warning: no summary found for: $uri'); | 327 print('Warning: no summary found for: $uri'); |
| 427 } | 328 } |
| 428 return result; | 329 return result; |
| 429 } | 330 } |
| 430 } | 331 } |
| 431 | 332 |
| 432 class _Scanner extends Scanner { | 333 class _Scanner extends Scanner { |
| 433 _Scanner(String contents) : super(new CharSequenceReader(contents)) { | 334 _Scanner(String contents) : super(new CharSequenceReader(contents)) { |
| 434 preserveComments = false; | 335 preserveComments = false; |
| 435 } | 336 } |
| 436 | 337 |
| 437 @override | 338 @override |
| 438 void reportError(errorCode, int offset, List<Object> arguments) { | 339 void reportError(errorCode, int offset, List<Object> arguments) { |
| 439 // ignore errors. | 340 // ignore errors. |
| 440 } | 341 } |
| 441 } | 342 } |
| OLD | NEW |