| Index: pkg/compiler/tool/perf.dart
|
| diff --git a/pkg/compiler/tool/perf.dart b/pkg/compiler/tool/perf.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..51f98d61021901b04ac80ef5644223be97e48c82
|
| --- /dev/null
|
| +++ b/pkg/compiler/tool/perf.dart
|
| @@ -0,0 +1,319 @@
|
| +// 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 dart2js and measure its performance.
|
| +library compiler.tool.perf;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:io';
|
| +
|
| +import 'package:compiler/compiler_new.dart';
|
| +import 'package:compiler/src/common.dart';
|
| +import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
|
| +import 'package:compiler/src/diagnostics/messages.dart'
|
| + show Message, MessageTemplate;
|
| +import 'package:compiler/src/io/source_file.dart';
|
| +import 'package:compiler/src/options.dart' show ParserOptions;
|
| +import 'package:compiler/src/options.dart';
|
| +import 'package:compiler/src/parser/element_listener.dart' show ScannerOptions;
|
| +import 'package:compiler/src/parser/listener.dart';
|
| +import 'package:compiler/src/parser/node_listener.dart' show NodeListener;
|
| +import 'package:compiler/src/parser/parser.dart' show Parser;
|
| +import 'package:compiler/src/parser/partial_parser.dart';
|
| +import 'package:compiler/src/platform_configuration.dart' as platform;
|
| +import 'package:compiler/src/scanner/scanner.dart';
|
| +import 'package:compiler/src/source_file_provider.dart';
|
| +import 'package:compiler/src/tokens/token.dart' show Token;
|
| +import 'package:package_config/discovery.dart' show findPackages;
|
| +import 'package:package_config/packages.dart' show Packages;
|
| +import 'package:package_config/src/util.dart' show checkValidPackageUri;
|
| +
|
| +/// Cumulative total number of chars scanned.
|
| +int scanTotalChars = 0;
|
| +
|
| +/// Cumulative time spent scanning.
|
| +Stopwatch scanTimer = new Stopwatch();
|
| +
|
| +/// Helper class used to load source files using dart2js's internal APIs.
|
| +_Loader loader;
|
| +
|
| +main(List<String> args) async {
|
| + // TODO(sigmund): provide sdk folder as well.
|
| + if (args.length < 2) {
|
| + print('usage: perf.dart <bench-id> <entry.dart>');
|
| + exit(1);
|
| + }
|
| + var totalTimer = new Stopwatch()..start();
|
| +
|
| + var bench = args[0];
|
| + var entryUri = Uri.base.resolve(args[1]);
|
| +
|
| + await setup(entryUri);
|
| +
|
| + if (bench == 'scan') {
|
| + Set<SourceFile> files = await scanReachableFiles(entryUri);
|
| + // TODO(sigmund): consider replacing the warmup with instrumented snapshots.
|
| + for (int i = 0; i < 10; i++) scanFiles(files);
|
| + } else if (bench == 'parse') {
|
| + Set<SourceFile> files = await scanReachableFiles(entryUri);
|
| + // TODO(sigmund): consider replacing the warmup with instrumented snapshots.
|
| + for (int i = 0; i < 10; i++) 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);
|
| +}
|
| +
|
| +Future setup(Uri entryUri) async {
|
| + var inputProvider = new CompilerSourceFileProvider();
|
| + var sdkLibraries = await platform.load(_platformConfigUri, inputProvider);
|
| + var packages = await findPackages(entryUri);
|
| + loader = new _Loader(inputProvider, sdkLibraries, packages);
|
| +}
|
| +
|
| +/// Load and scans all files we need to process: files reachable from the
|
| +/// entrypoint and all core libraries automatically included by the VM.
|
| +Future<Set<SourceFile>> scanReachableFiles(Uri entryUri) async {
|
| + var files = new Set<SourceFile>();
|
| + var loadTimer = new Stopwatch()..start();
|
| + var entrypoints = [
|
| + entryUri,
|
| + Uri.parse("dart:async"),
|
| + Uri.parse("dart:collection"),
|
| + Uri.parse("dart:convert"),
|
| + Uri.parse("dart:core"),
|
| + Uri.parse("dart:developer"),
|
| + Uri.parse("dart:_internal"),
|
| + Uri.parse("dart:io"),
|
| + Uri.parse("dart:isolate"),
|
| + Uri.parse("dart:math"),
|
| + Uri.parse("dart:mirrors"),
|
| + Uri.parse("dart:typed_data"),
|
| + ];
|
| + for (var entry in entrypoints) {
|
| + await collectSources(await loader.loadFile(entry), files);
|
| + }
|
| + loadTimer.stop();
|
| +
|
| + print('input size: ${scanTotalChars} chars');
|
| + var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds;
|
| + report("load", loadTime);
|
| + report("scan", scanTimer.elapsedMicroseconds);
|
| + return files;
|
| +}
|
| +
|
| +/// Scans every file in [files] and reports the time spent doing so.
|
| +void scanFiles(Set<SourceFile> 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 old = scanTotalChars;
|
| + scanTotalChars = 0;
|
| + for (var source in files) {
|
| + tokenize(source);
|
| + }
|
| +
|
| + // Report size and scanning time again. See discussion above.
|
| + if (old != scanTotalChars) print('input size changed? ${old} chars');
|
| + report("scan", scanTimer.elapsedMicroseconds);
|
| +}
|
| +
|
| +/// Parses every file in [files] and reports the time spent doing so.
|
| +void parseFiles(Set<SourceFile> 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 old = 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 (old != scanTotalChars) print('input size changed? ${old} chars');
|
| + report("scan", scanTimer.elapsedMicroseconds);
|
| +
|
| + report(
|
| + "parse", parseTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds);
|
| +}
|
| +
|
| +/// Add to [files] all sources reachable from [start].
|
| +Future collectSources(SourceFile start, Set<SourceFile> files) async {
|
| + if (!files.add(start)) return;
|
| + for (var directive in parseDirectives(start)) {
|
| + var next = await loader.loadFile(start.uri.resolve(directive));
|
| + await collectSources(next, files);
|
| + }
|
| +}
|
| +
|
| +/// Uses the diet-parser to parse only directives in [source], returns the
|
| +/// URIs seen in import/export/part directives in the file.
|
| +Set<String> parseDirectives(SourceFile source) {
|
| + var tokens = tokenize(source);
|
| + var listener = new DirectiveListener();
|
| + new PartialParser(listener, const _ParserOptions()).parseUnit(tokens);
|
| + return listener.targets;
|
| +}
|
| +
|
| +/// Parse the full body of [source].
|
| +parseFull(SourceFile source) {
|
| + var tokens = tokenize(source);
|
| + NodeListener listener = new NodeListener(
|
| + const ScannerOptions(canUseNative: true), new FakeReporter(), null);
|
| + Parser parser = new Parser(listener, const _ParserOptions());
|
| + parser.parseUnit(tokens);
|
| + return listener.popNode();
|
| +}
|
| +
|
| +/// Scan [source] and return the first token produced by the scanner.
|
| +Token tokenize(SourceFile source) {
|
| + scanTimer.start();
|
| + scanTotalChars += source.length;
|
| + var token = new Scanner(source).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 us, ${time ~/ 1000} ms');
|
| + sb.write(', ${scanTotalChars * 1000 ~/ time} chars/ms');
|
| + print('$sb');
|
| +}
|
| +
|
| +/// Listener that parses out just the uri in imports, exports, and part
|
| +/// directives.
|
| +class DirectiveListener extends Listener {
|
| + Set<String> targets = new Set<String>();
|
| +
|
| + bool inDirective = false;
|
| + void enterDirective() {
|
| + inDirective = true;
|
| + }
|
| +
|
| + void exitDirective() {
|
| + inDirective = false;
|
| + }
|
| +
|
| + void beginImport(Token importKeyword) => enterDirective();
|
| + void beginExport(Token token) => enterDirective();
|
| + void beginPart(Token token) => enterDirective();
|
| +
|
| + void beginLiteralString(Token token) {
|
| + if (inDirective) {
|
| + var quotedString = token.value;
|
| + targets.add(quotedString.substring(1, quotedString.length - 1));
|
| + }
|
| + }
|
| +
|
| + void endExport(Token exportKeyword, Token semicolon) => exitDirective();
|
| + void endImport(Token importKeyword, Token deferredKeyword, Token asKeyword,
|
| + Token semicolon) =>
|
| + exitDirective();
|
| + void endPart(Token partKeyword, Token semicolon) => exitDirective();
|
| +}
|
| +
|
| +Uri _libraryRoot = Platform.script.resolve('../../../sdk/');
|
| +Uri _platformConfigUri = _libraryRoot.resolve("lib/dart_server.platform");
|
| +
|
| +class FakeReporter extends DiagnosticReporter {
|
| + final hasReportedError = false;
|
| + final options = new FakeReporterOptions();
|
| +
|
| + withCurrentElement(e, f) => f();
|
| + log(m) => print(m);
|
| + internalError(_, m) => print(m);
|
| + spanFromSpannable(_) => null;
|
| +
|
| + void reportError(DiagnosticMessage message,
|
| + [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
|
| + print('error: ${message.message}');
|
| + }
|
| +
|
| + void reportWarning(DiagnosticMessage message,
|
| + [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
|
| + print('warning: ${message.message}');
|
| + }
|
| +
|
| + void reportHint(DiagnosticMessage message,
|
| + [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) {
|
| + print('hint: ${message.message}');
|
| + }
|
| +
|
| + void reportInfo(_, __, [Map arguments = const {}]) {}
|
| +
|
| + DiagnosticMessage createMessage(_, MessageKind kind,
|
| + [Map arguments = const {}]) {
|
| + MessageTemplate template = MessageTemplate.TEMPLATES[kind];
|
| + Message message = template.message(arguments, false);
|
| + return new DiagnosticMessage(null, null, message);
|
| + }
|
| +}
|
| +
|
| +class FakeReporterOptions {
|
| + bool get suppressHints => false;
|
| + bool get hidePackageWarnings => false;
|
| +}
|
| +
|
| +class _Loader {
|
| + CompilerInput inputProvider;
|
| +
|
| + /// Maps dart-URIs to a known location in the sdk.
|
| + Map<String, Uri> sdkLibraries;
|
| + Map<Uri, SourceFile> _cache = {};
|
| + Packages packages;
|
| +
|
| + _Loader(this.inputProvider, this.sdkLibraries, this.packages);
|
| +
|
| + Future<SourceFile> loadFile(Uri uri) async {
|
| + if (!uri.isAbsolute) throw 'Relative uri $uri provided to readScript.';
|
| + Uri resourceUri = _translateUri(uri);
|
| + if (resourceUri == null || resourceUri.scheme == 'dart-ext') {
|
| + throw '$uri not resolved or unsupported.';
|
| + }
|
| + var file = _cache[resourceUri];
|
| + if (file != null) return _cache[resourceUri];
|
| + return _cache[resourceUri] = await _readFile(resourceUri);
|
| + }
|
| +
|
| + Future<SourceFile> _readFile(Uri uri) async {
|
| + var data = await inputProvider.readFromUri(uri);
|
| + if (data is List<int>) return new Utf8BytesSourceFile(uri, data);
|
| + if (data is String) return new StringSourceFile.fromUri(uri, data);
|
| + // TODO(sigmund): properly handle errors, just report, return null, wrap
|
| + // above and continue...
|
| + throw "Expected a 'String' or a 'List<int>' from the input "
|
| + "provider, but got: ${data.runtimeType}.";
|
| + }
|
| +
|
| + Uri _translateUri(Uri uri) {
|
| + if (uri.scheme == 'dart') return sdkLibraries[uri.path];
|
| + if (uri.scheme == 'package') return _translatePackageUri(uri);
|
| + return uri;
|
| + }
|
| +
|
| + Uri _translatePackageUri(Uri uri) {
|
| + checkValidPackageUri(uri);
|
| + return packages.resolve(uri, notFound: (_) {
|
| + print('$uri not found');
|
| + });
|
| + }
|
| +}
|
| +
|
| +class _ParserOptions implements ParserOptions {
|
| + const _ParserOptions();
|
| + bool get enableGenericMethodSyntax => true;
|
| +}
|
|
|