9a243b35eb
This was needed back when we thought the front end would be based on the analyzer, and so we thought its performance characteristics would depend on those of the analyzer summary mechanism. But since the front end is not based on the analyzer, and the performance of the analyzer summary mechanism is performance tested elsewhere, we can remove this code. Removing it will make it easier to do some re-work of summaries that is needed for Dart 2.1 support. Change-Id: I4aa8b5251f26ba7fd59b99d325c66da642d410ed Reviewed-on: https://dart-review.googlesource.com/71661 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
239 lines
7.6 KiB
Dart
239 lines
7.6 KiB
Dart
// 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.
|
|
///
|
|
/// TODO(sigmund): rename to 'analyzer_perf.dart' in sync with changes to the
|
|
/// perf bots.
|
|
///
|
|
/// This file was started to measure the implementation of the front-end when it
|
|
/// was based on the analyzer codebase. Now that we are using fasta as the
|
|
/// implementation (which is measured in fasta_perf.dart), we still want to
|
|
/// measure the analyzer to ensure that there are no regressions when replacing
|
|
/// features (e.g. there is no regression from replacing summaries with kernel
|
|
/// outlines).
|
|
library front_end.tool.perf;
|
|
|
|
import 'dart:async';
|
|
import 'dart:io' show Directory, File, Platform, exit;
|
|
|
|
import 'package:analyzer/dart/ast/ast.dart';
|
|
import 'package:analyzer/error/listener.dart';
|
|
import 'package:analyzer/file_system/physical_file_system.dart'
|
|
show PhysicalResourceProvider;
|
|
import 'package:analyzer/source/package_map_resolver.dart';
|
|
import 'package:analyzer/src/context/builder.dart';
|
|
import 'package:analyzer/src/dart/sdk/sdk.dart' show FolderBasedDartSdk;
|
|
import 'package:analyzer/src/file_system/file_system.dart';
|
|
import 'package:analyzer/src/generated/parser.dart';
|
|
import 'package:analyzer/src/generated/source.dart';
|
|
import 'package:analyzer/src/generated/source_io.dart';
|
|
import 'package:front_end/src/fasta/scanner.dart';
|
|
import 'package:front_end/src/scanner/token.dart';
|
|
import 'package:package_config/discovery.dart';
|
|
import 'package:path/path.dart' as path;
|
|
|
|
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 bench = args[0];
|
|
var entryUri = Uri.base.resolve(args[1]);
|
|
|
|
await setup(entryUri);
|
|
|
|
Set<Source> files = scanReachableFiles(entryUri);
|
|
var handlers = {
|
|
'scan': () async => scanFiles(files),
|
|
'parse': () async => parseFiles(files),
|
|
};
|
|
|
|
var handler = handlers[bench];
|
|
if (handler == null) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// Cumulative time spent parsing.
|
|
Stopwatch parseTimer = new Stopwatch();
|
|
|
|
/// Cumulative time spent scanning.
|
|
Stopwatch scanTimer = new Stopwatch();
|
|
|
|
/// Size of all sources.
|
|
int inputSize = 0;
|
|
|
|
/// Factory to load and resolve app, packages, and sdk sources.
|
|
SourceFactory sources;
|
|
|
|
/// Path to the root of the built SDK that is being used to execute this script.
|
|
final _sdkPath = _findSdkPath();
|
|
|
|
/// 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) {
|
|
var next = sources.resolveUri(start, directive.uri.stringValue);
|
|
collectSources(next, files);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Uses the diet-parser to parse only directives in [source].
|
|
CompilationUnit parseDirectives(Source source) {
|
|
var token = tokenize(source);
|
|
// TODO(jcollins-g): Make parser work with Fasta
|
|
var parser =
|
|
new Parser(source, AnalysisErrorListener.NULL_LISTENER, useFasta: false);
|
|
return parser.parseDirectives(token);
|
|
}
|
|
|
|
/// Parses every file in [files] and reports the time spent doing so.
|
|
void parseFiles(Set<Source> files) {
|
|
scanTimer = new Stopwatch();
|
|
parseTimer = new Stopwatch();
|
|
for (var source in files) {
|
|
parseFull(source);
|
|
}
|
|
|
|
report('scan', scanTimer.elapsedMicroseconds);
|
|
report('parse', parseTimer.elapsedMicroseconds);
|
|
}
|
|
|
|
/// Parse the full body of [source] and return it's compilation unit.
|
|
CompilationUnit parseFull(Source source) {
|
|
var token = tokenize(source);
|
|
parseTimer.start();
|
|
var parser = new Parser(source, AnalysisErrorListener.NULL_LISTENER);
|
|
var unit = parser.parseCompilationUnit(token);
|
|
parseTimer.stop();
|
|
return unit;
|
|
}
|
|
|
|
/// 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');
|
|
}
|
|
|
|
/// Scans every file in [files] and reports the time spent doing so.
|
|
void scanFiles(Set<Source> files) {
|
|
// `tokenize` records how many chars are scanned and how long it takes to scan
|
|
// them. As this function is called repeatedly when running as a benchmark, we
|
|
// make sure to clear the data and compute it again every time.
|
|
scanTimer = new Stopwatch();
|
|
for (var source in files) {
|
|
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.
|
|
Set<Source> scanReachableFiles(Uri entryUri) {
|
|
var files = new Set<Source>();
|
|
var loadTimer = new Stopwatch()..start();
|
|
scanTimer = new Stopwatch();
|
|
collectSources(sources.forUri2(entryUri), files);
|
|
|
|
var libs = [
|
|
'dart:async',
|
|
'dart:collection',
|
|
'dart:convert',
|
|
'dart:core',
|
|
'dart:developer',
|
|
'dart:_internal',
|
|
'dart:isolate',
|
|
'dart:math',
|
|
'dart:mirrors',
|
|
'dart:typed_data',
|
|
'dart:io'
|
|
];
|
|
|
|
for (var lib in libs) {
|
|
collectSources(sources.forUri(lib), files);
|
|
}
|
|
|
|
loadTimer.stop();
|
|
|
|
inputSize = 0;
|
|
for (var s in files) inputSize += s.contents.data.length;
|
|
print('input size: ${inputSize} chars');
|
|
var loadTime = loadTimer.elapsedMicroseconds - scanTimer.elapsedMicroseconds;
|
|
report('load', loadTime);
|
|
report('scan', scanTimer.elapsedMicroseconds);
|
|
return files;
|
|
}
|
|
|
|
/// Sets up analyzer to be able to load and resolve app, packages, and sdk
|
|
/// sources.
|
|
Future setup(Uri entryUri) async {
|
|
var provider = PhysicalResourceProvider.INSTANCE;
|
|
var packageMap = new ContextBuilder(provider, null, null)
|
|
.convertPackagesToMap(await findPackages(entryUri));
|
|
sources = new SourceFactory([
|
|
new ResourceUriResolver(provider),
|
|
new PackageMapUriResolver(provider, packageMap),
|
|
new DartUriResolver(
|
|
new FolderBasedDartSdk(provider, provider.getFolder(_sdkPath))),
|
|
]);
|
|
}
|
|
|
|
/// Scan [source] and return the first token produced by the scanner.
|
|
Token tokenize(Source source) {
|
|
scanTimer.start();
|
|
// TODO(sigmund): is there a way to scan from a random-access-file without
|
|
// first converting to String?
|
|
ScannerResult result =
|
|
scanString(source.contents.data, includeComments: false);
|
|
var token = result.tokens;
|
|
if (result.hasErrors) {
|
|
// Ignore errors.
|
|
while (token is ErrorToken) {
|
|
token = token.next;
|
|
}
|
|
}
|
|
scanTimer.stop();
|
|
return token;
|
|
}
|
|
|
|
String _findSdkPath() {
|
|
var executable = Platform.resolvedExecutable;
|
|
var executableDir = path.dirname(executable);
|
|
for (var candidate in [
|
|
path.dirname(executableDir),
|
|
path.join(executableDir, 'dart-sdk')
|
|
]) {
|
|
if (new File(path.join(candidate, 'lib', 'dart_server.platform'))
|
|
.existsSync()) {
|
|
return candidate;
|
|
}
|
|
}
|
|
// Not found; guess "sdk" relative to the current directory.
|
|
return new Directory('sdk').absolute.path;
|
|
}
|