Chromium Code Reviews| Index: pkg/analyzer_cli/tool/perf.dart |
| diff --git a/pkg/analyzer_cli/tool/perf.dart b/pkg/analyzer_cli/tool/perf.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..268b0b7d3be676c86761df1dce01a848a30aa046 |
| --- /dev/null |
| +++ b/pkg/analyzer_cli/tool/perf.dart |
| @@ -0,0 +1,169 @@ |
| +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +/// An entrypoint used to run portions of analyzer and measure its performance. |
| +library analyzer_cli.tool.perf; |
| + |
| +import 'dart:io' show exit; |
| +import 'package:analyzer/dart/ast/ast.dart'; |
| +import 'package:analyzer/dart/ast/token.dart'; |
| +import 'package:analyzer/file_system/file_system.dart' |
| + show ResourceProvider, ResourceUriResolver |
| + hide File; |
| +import 'package:analyzer/file_system/physical_file_system.dart' |
| + show PhysicalResourceProvider; |
| +import 'package:analyzer/src/dart/scanner/reader.dart'; |
| +import 'package:analyzer/src/dart/scanner/scanner.dart'; |
| +import 'package:analyzer/src/dart/sdk/sdk.dart' show FolderBasedDartSdk; |
| +import 'package:analyzer/src/error.dart'; |
| +import 'package:analyzer/src/generated/error.dart'; |
| +import 'package:analyzer/src/generated/java_io.dart' show JavaFile; |
| +import 'package:analyzer/src/generated/parser.dart'; |
| +import 'package:analyzer/src/generated/source.dart'; |
| +import 'package:analyzer/src/generated/source_io.dart'; |
| + |
| +/// Cummulative total number of chars scanned. |
| +int scanTotalChars = 0; |
| + |
| +/// Cummulative time spent scanning. |
| +Stopwatch scanTimer = new Stopwatch(); |
| + |
| +/// Factory to load and resolve app, packages, and sdk sources. |
| +SourceFactory sources; |
| + |
| +main(args) { |
| + // TODO(sigmund): provide sdk folder as well. |
| + if (args.length < 2) { |
| + print('usage: perf.dart <bench-id> <package-root> <entry.dart>'); |
| + exit(1); |
| + } |
| + var totalTimer = new Stopwatch()..start(); |
| + |
| + var bench = args[0]; |
| + var packageRoot = args[1]; |
| + var entryUri = Uri.base.resolve(args[2]); |
| + |
| + setup(packageRoot); |
| + if (bench == 'scan') { |
| + scanReachableFiles(entryUri); |
| + } else if (bench == 'parse') { |
| + Set<Source> files = scanReachableFiles(entryUri); |
| + parseFiles(files); |
| + } else { |
| + print('unsupported bench-id: $bench. Please specify "scan" or "parse"'); |
| + // TODO(sigmund): implement the remaining benchmarks. |
| + exit(1); |
| + } |
| + |
| + totalTimer.stop(); |
| + report("Total", totalTimer.elapsedMicroseconds); |
| +} |
| + |
| +/// Sets up analyzer to be able to load and resolve app, packages, and sdk |
| +/// sources. |
| +void setup(String packageRoot) { |
| + var provider = PhysicalResourceProvider.INSTANCE; |
| + sources = new SourceFactory([ |
| + new ResourceUriResolver(provider), |
| + new PackageUriResolver([new JavaFile(packageRoot)]), |
| + new DartUriResolver( |
| + new FolderBasedDartSdk(provider, provider.getFolder("sdk"))), |
| + ]); |
| +} |
| + |
| +/// Load and scans all files we need to process: files reachable from the |
| +/// entrypoint and all core libraries automatically included by the VM. |
| +Set<Source> scanReachableFiles(Uri entryUri) { |
| + var files = new Set<Source>(); |
| + var loadTimer = new Stopwatch()..start(); |
| + collectSources(sources.forUri2(entryUri), files); |
| + collectSources(sources.forUri("dart:async"), files); |
| + collectSources(sources.forUri("dart:collection"), files); |
| + collectSources(sources.forUri("dart:convert"), files); |
| + collectSources(sources.forUri("dart:core"), files); |
| + collectSources(sources.forUri("dart:developer"), files); |
| + collectSources(sources.forUri("dart:_internal"), files); |
| + collectSources(sources.forUri("dart:isolate"), files); |
| + collectSources(sources.forUri("dart:math"), files); |
| + collectSources(sources.forUri("dart:mirrors"), files); |
| + collectSources(sources.forUri("dart:typed_data"), files); |
| + loadTimer.stop(); |
| + |
| + print('input size: ${scanTotalChars} chars'); |
| + var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; |
| + report("Loader", loadTime); |
| + report("Scanner", scanTimer.elapsedMicroseconds); |
| + return files; |
| +} |
| + |
| +/// Parses every file in [files] and reports the time spent doing so. |
| +void parseFiles(Set<Source> files) { |
| + // The code below will record again how many chars are scanned and how long it |
| + // takes to scan them, even though we already did so in [scanReachableFiles]. |
| + // Recording and reporting this twice is unnecessary, but we do so for now to |
| + // validate that the results are consistent. |
| + scanTimer = new Stopwatch(); |
| + var oldTotal = scanTotalChars; |
| + scanTotalChars = 0; |
| + var parseTimer = new Stopwatch()..start(); |
| + for (var source in files) { |
| + parseFull(source); |
| + } |
| + parseTimer.stop(); |
| + |
| + // Report size and scanning time again. See discussion above. |
| + if (oldTotal != scanTotalChars) print('input size changed? ${total} chars'); |
| + report("Scanner", scanTimer.elapsedMicroseconds); |
| + |
| + var pTime = parseTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds; |
| + report("Parser", pTime); |
| +} |
| + |
| +/// Add to [files] all sources reachable from [start]. |
| +void collectSources(Source start, Set<Source> files) { |
| + if (!files.add(start)) return; |
| + var unit = parseDirectives(start); |
| + for (var directive in unit.directives) { |
| + if (directive is! UriBasedDirective) continue; |
| + var next = sources.resolveUri(start, directive.uri.stringValue); |
|
Brian Wilkerson
2016/09/16 15:32:02
This introduces a compilation error:
ERROR: The g
|
| + collectSources(next, files); |
| + } |
| +} |
| + |
| +/// Uses the diet-parser to parse only directives in [source]. |
| +CompilationUnit parseDirectives(Source source) { |
| + var token = tokenize(source); |
| + var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); |
| + return parser.parseDirectives(token); |
| +} |
| + |
| +/// Parse the full body of [source] and return it's compilation unit. |
| +CompilationUnit parseFull(Source source) { |
| + var token = tokenize(source); |
| + var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER); |
| + return parser.parseCompilationUnit(token); |
| +} |
| + |
| +/// Scan [source] and return the first token produced by the scanner. |
| +Token tokenize(Source source) { |
| + scanTimer.start(); |
| + var contents = source.contents.data; |
| + scanTotalChars += contents.length; |
| + // TODO(sigmund): is there a way to scan from a random-access-file without |
| + // first converting to String? |
| + var scanner = new Scanner(source, new CharSequenceReader(contents), |
| + AnalysisErrorListener.NULL_LISTENER); |
| + var token = scanner.tokenize(); |
| + scanTimer.stop(); |
| + return token; |
| +} |
| + |
| +/// Report that metric [name] took [time] micro-seconds to process |
| +/// [scanTotalChars] characters. |
| +void report(String name, int time) { |
| + var sb = new StringBuffer(); |
| + sb.write('$name: ${time ~/ 1000} ms'); |
| + sb.write(', ${scanTotalChars * 1000 ~/ time} chars/ms'); |
| + print('$sb'); |
| +} |