Files
sdk/pkg/front_end/tool/fasta_perf.dart
T
Peter von der Ahé 032925c4ff Remove obsolete features
Remove tree-shaker as we're not using it, and it creates problems
when the SDK changes.

Remove scopes from AstBuilder as we will be phasing out AstBuilder
in favor of using BodyBuilder and the Forest API.

Change-Id: I51950c5d8bfb0493587336325f9da8f5bf8ecea6
Reviewed-on: https://dart-review.googlesource.com/57261
Commit-Queue: Peter von der Ahé <ahe@google.com>
Reviewed-by: Dan Rubel <danrubel@google.com>
2018-05-31 07:24:54 +00:00

285 lines
9.5 KiB
Dart

// Copyright (c) 2017, 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 fasta and measure its performance.
library front_end.tool.fasta_perf;
import 'dart:async';
import 'dart:io';
import 'package:analyzer/src/fasta/ast_builder.dart';
import 'package:args/args.dart';
import 'package:front_end/src/api_prototype/front_end.dart';
import 'package:front_end/src/base/processed_options.dart';
import 'package:front_end/src/fasta/parser.dart';
import 'package:front_end/src/fasta/scanner.dart';
import 'package:front_end/src/fasta/scanner/io.dart' show readBytesFromFileSync;
import 'package:front_end/src/fasta/source/directive_listener.dart';
import 'package:front_end/src/fasta/uri_translator.dart' show UriTranslator;
import 'perf_common.dart';
/// Cumulative total number of chars scanned.
int inputSize = 0;
/// Cumulative time spent scanning.
Stopwatch scanTimer = new Stopwatch();
main(List<String> args) async {
// TODO(sigmund): provide sdk folder as well.
var options = argParser.parse(args);
if (options.rest.length != 2) {
print('usage: fasta_perf.dart [options] <bench-id> <entry.dart>');
print(argParser.usage);
exit(1);
}
bool strongMode = !options['legacy'];
var bench = options.rest[0];
var entryUri = Uri.base.resolve(options.rest[1]);
await setup(entryUri);
Map<Uri, List<int>> files = await scanReachableFiles(entryUri);
var handlers = {
'scan': () async => scanFiles(files),
// TODO(sigmund): enable when we can run the ast-builder standalone.
// 'parse': () async => parseFiles(files),
'kernel_gen_e2e': () async {
await generateKernel(entryUri, strongMode: strongMode);
},
'kernel_gen_e2e_sum': () async {
await generateKernel(entryUri, compileSdk: false, strongMode: strongMode);
},
};
var handler = handlers[bench];
if (handler == null) {
// TODO(sigmund): implement the remaining benchmarks.
print('unsupported bench-id: $bench. Please specify one of the following: '
'${handlers.keys.join(", ")}');
exit(1);
}
// TODO(sigmund): replace the warmup with instrumented snapshots.
int iterations = bench.contains('kernel_gen') ? 2 : 10;
for (int i = 0; i < iterations; i++) {
var totalTimer = new Stopwatch()..start();
print('== iteration $i');
await handler();
totalTimer.stop();
report('total', totalTimer.elapsedMicroseconds);
}
}
Uri sdkRoot = _computeRoot();
Uri _computeRoot() {
// TODO(sigmund): delete this when our performance bots include runtime/lib/
if (new Directory('runtime/lib/').existsSync()) {
return Uri.base.resolve("sdk/");
}
return Uri.base
.resolveUri(new Uri.file(Platform.resolvedExecutable))
.resolve('patched_sdk/');
}
/// Translates `dart:*` and `package:*` URIs to resolved URIs.
UriTranslator uriResolver;
/// Preliminary set up to be able to correctly resolve URIs on the given
/// program.
Future setup(Uri entryUri) async {
var options = new CompilerOptions()
..sdkRoot = sdkRoot
// Because this is only used to create a uriResolver, we don't allow any
// whitelisting of error messages in the error handler.
..onError = onErrorHandler(false)
..compileSdk = true
..packagesFileUri = Uri.base.resolve('.packages');
uriResolver = await new ProcessedOptions(options).getUriTranslator();
}
/// Scan [contents] and return the first token produced by the scanner.
Token tokenize(List<int> contents) {
scanTimer.start();
var token = scan(contents).tokens;
scanTimer.stop();
return token;
}
/// Scans every file in [files] and reports the time spent doing so.
void scanFiles(Map<Uri, List<int>> files) {
scanTimer = new Stopwatch();
for (var source in files.values) {
tokenize(source);
}
report('scan', scanTimer.elapsedMicroseconds);
}
/// Load and scans all files we need to process: files reachable from the
/// entrypoint and all core libraries automatically included by the VM.
Future<Map<Uri, List<int>>> scanReachableFiles(Uri entryUri) async {
var files = <Uri, List<int>>{};
var loadTimer = new Stopwatch()..start();
scanTimer = new Stopwatch();
var entrypoints = [
entryUri,
// These extra libraries are added to match the same set of libraries
// scanned by default by the VM and the other benchmarks.
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(entry, files);
}
loadTimer.stop();
inputSize = 0;
// adjust size because there is a null-terminator on the contents.
for (var source in files.values) inputSize += (source.length - 1);
print('input size: $inputSize chars');
var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds;
report('load', loadTime);
report('scan', scanTimer.elapsedMicroseconds);
return files;
}
/// Add to [files] all sources reachable from [start].
Future<Null> collectSources(Uri start, Map<Uri, List<int>> files) async {
helper(Uri uri) {
uri = uriResolver.translate(uri) ?? uri;
if (uri == null) return;
if (files.containsKey(uri)) return;
var contents = readBytesFromFileSync(uri);
files[uri] = contents;
for (var directiveUri in extractDirectiveUris(contents)) {
helper(uri.resolve(directiveUri));
}
}
helper(start);
}
/// Parse [contents] as a Dart program and return the URIs that appear in its
/// import, export, and part directives.
Set<String> extractDirectiveUris(List<int> contents) {
var listener = new DirectiveListenerWithNative();
new TopLevelParser(listener).parseUnit(tokenize(contents));
return new Set<String>()
..addAll(listener.imports.map((directive) => directive.uri))
..addAll(listener.exports.map((directive) => directive.uri))
..addAll(listener.parts);
}
class DirectiveListenerWithNative extends DirectiveListener {
@override
void handleNativeFunctionBodySkipped(Token nativeToken, Token semicolon) {
// Always allow native functions.
}
}
/// Parses every file in [files] and reports the time spent doing so.
void parseFiles(Map<Uri, List<int>> files) {
scanTimer = new Stopwatch();
var parseTimer = new Stopwatch()..start();
files.forEach((uri, source) {
parseFull(uri, source);
});
parseTimer.stop();
report('scan', scanTimer.elapsedMicroseconds);
report(
'parse', parseTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds);
}
/// Parse the full body of [source].
parseFull(Uri uri, List<int> source) {
var tokens = tokenize(source);
Parser parser = new Parser(new _PartialAstBuilder(uri));
parser.parseUnit(tokens);
}
// Note: AstBuilder doesn't build compilation-units or classes, only method
// bodies. So this listener is not feature complete.
class _PartialAstBuilder extends AstBuilder {
_PartialAstBuilder(Uri uri) : super(null, null, true, uri);
// Note: this method converts the body to kernel, so we skip that here.
@override
finishFunction(annotations, formals, asyncModifier, body) {}
}
// Invoke the fasta kernel generator for the program starting in [entryUri]
generateKernel(Uri entryUri,
{bool compileSdk: true, bool strongMode: false}) async {
// TODO(sigmund): this is here only to compute the input size,
// we should extract the input size from the frontend instead.
scanReachableFiles(entryUri);
var timer = new Stopwatch()..start();
var options = new CompilerOptions()
..sdkRoot = sdkRoot
..reportMessages = true
..onError = onErrorHandler(strongMode)
..strongMode = strongMode
..target = createTarget(isFlutter: false, strongMode: strongMode)
..packagesFileUri = Uri.base.resolve('.packages')
..compileSdk = compileSdk;
if (!compileSdk) {
// TODO(sigmund): fix this: this is broken since the change to move .dill
// files out of the patched_sdk folder. It is not failing anywhere because
// this codepath is not used right now in our performance bots.
options.sdkSummary = sdkRoot.resolve('outline.dill');
}
var entrypoints = [
entryUri,
// These extra libraries are added to match the same set of libraries
// scanned by default by the VM and the other benchmarks.
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'),
];
var program = await kernelForComponent(entrypoints, options);
timer.stop();
var name = 'kernel_gen_e2e${compileSdk ? "" : "_sum"}';
report(name, timer.elapsedMicroseconds);
return program;
}
/// Report that metric [name] took [time] micro-seconds to process
/// [inputSize] characters.
void report(String name, int time) {
var sb = new StringBuffer();
var padding = ' ' * (20 - name.length);
sb.write('$name:$padding $time us, ${time ~/ 1000} ms');
var invSpeed = (time * 1000 / inputSize).toStringAsFixed(2);
sb.write(', $invSpeed ns/char');
print('$sb');
}
ArgParser argParser = new ArgParser()
..addFlag('legacy',
help: 'run the compiler in legacy-mode',
defaultsTo: false,
negatable: false);