Files
sdk/pkg/front_end/tool/incremental_perf.dart
T
Sigmund Cherem 9b44fc39c8 Deprecate chaseDependencies option.
The option is not used by any client at this time and removing it simplifies the
next stack of changes I'll be making on how we compose file systems.

I also don't believe we will necessarily add this back as an option, but instead
it might be that clients requriing hermeticity will provide a custom file system
that helps with that.

Change-Id: I401efb042920d234382b6c041b13d40ffae5c908
Reviewed-on: https://dart-review.googlesource.com/56462
Commit-Queue: Peter von der Ahé <ahe@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
2018-05-24 04:57:01 +00:00

332 lines
11 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 measure performance of the incremental compiler.
///
/// Given an input a program and a .json file describing edits, this script will
/// first compile the program, then apply edits, recompile the
/// program, and report relevant metrics.
///
/// The edits are encoded as a JSON array:
/// - Each entry in the array is an iteration of edits and holds a list of
/// individual edits. All changes in one iteration are applied at once
/// before calling [IncrementalKernelGenerator.computeDelta].
///
/// - Each edit is a triple declaring a string replacement operation:
/// [uri, from, to]
///
/// Edits are applied in order, so more than on edit is allowed on the same
/// file.
///
/// For example:
/// [
/// {
/// "name" : "big_change",
/// "edits" : [
/// ["input1.dart", "black", "green"],
/// ["input1.dart", "30px", "10px"],
/// ["input2.dart", "a.toString()", ""$a""]
/// ]
/// },
/// {
/// "name" : "small_chnage",
/// "edits" : [
/// ["input1.dart", "green", "blue"]
/// ]
/// }
/// ]
///
/// Is interpreted as 2 iterations, the first iteration updates input1.dart
/// with 2 changes, and input2.dart with one change. The second iteration
/// updates input1.dart a second time.
library front_end.tool.incremental_perf;
import 'dart:async';
import 'dart:convert';
import 'dart:io' hide FileSystemEntity;
import 'package:args/args.dart';
import 'package:front_end/src/api_prototype/byte_store.dart';
import 'package:front_end/src/api_prototype/file_system.dart'
show FileSystemEntity;
import 'package:front_end/src/api_prototype/front_end.dart';
import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart';
import 'package:front_end/src/api_prototype/memory_file_system.dart';
import 'package:front_end/src/api_prototype/standard_file_system.dart';
import 'package:front_end/src/base/processed_options.dart';
import 'package:front_end/src/byte_store/protected_file_byte_store.dart';
import 'package:front_end/src/fasta/uri_translator.dart';
import 'perf_common.dart';
main(List<String> args) async {
var options = argParser.parse(args);
if (options.rest.length != 2) {
throw """
usage: incremental_perf.dart [options] <entry.dart> <edits.json>
${argParser.usage}""";
}
var entryUri = _resolveOverlayUri(options.rest[0]);
var editsUri = Uri.base.resolve(options.rest[1]);
var changeSets =
parse(jsonDecode(new File.fromUri(editsUri).readAsStringSync()));
bool verbose = options["verbose"];
bool verboseCompilation = options["verbose-compilation"];
bool strongMode = options["mode"] == "strong";
bool isFlutter = options["target"] == "flutter";
bool useMinimalGenerator = options["implementation"] == "minimal";
TimingsCollector collector = new TimingsCollector(verbose);
for (int i = 0; i < 8; i++) {
await benchmark(
collector,
entryUri,
strongMode,
isFlutter,
useMinimalGenerator,
verbose,
verboseCompilation,
changeSets,
options["sdk-summary"],
options["sdk-library-specification"],
options["cache"]);
if (!options["loop"]) break;
}
collector.printTimings();
}
Future benchmark(
TimingsCollector collector,
Uri entryUri,
bool strongMode,
bool isFlutter,
bool useMinimalGenerator,
bool verbose,
bool verboseCompilation,
List<ChangeSet> changeSets,
String sdkSummary,
String sdkLibrarySpecification,
String cache) async {
var overlayFs = new OverlayFileSystem();
var compilerOptions = new CompilerOptions()
..verbose = verboseCompilation
..fileSystem = overlayFs
..strongMode = strongMode
..onError = onErrorHandler(strongMode)
..target = createTarget(isFlutter: isFlutter, strongMode: strongMode);
if (sdkSummary != null) {
compilerOptions.sdkSummary = _resolveOverlayUri(sdkSummary);
}
if (sdkLibrarySpecification != null) {
compilerOptions.librariesSpecificationUri =
_resolveOverlayUri(sdkLibrarySpecification);
}
var dir = Directory.systemTemp.createTempSync("ikg-cache");
compilerOptions.byteStore = createByteStore(cache, dir.path);
final processedOptions = new ProcessedOptions(compilerOptions, [entryUri]);
final UriTranslator uriTranslator = await processedOptions.getUriTranslator();
collector.start("Initial compilation");
var generator = new IncrementalKernelGenerator(compilerOptions, entryUri);
var component = await generator.computeDelta();
collector.stop("Initial compilation");
if (verbose) {
print("Libraries changed: ${component.libraries.length}");
}
if (component.libraries.length < 1) {
throw "No libraries were changed";
}
for (final ChangeSet changeSet in changeSets) {
String name = "Change '${changeSet.name}' - Incremental compilation";
await applyEdits(
changeSet.edits, overlayFs, generator, uriTranslator, verbose);
collector.start(name);
component = await generator.computeDelta();
collector.stop(name);
if (verbose) {
print("Change '${changeSet.name}' - "
"Libraries changed: ${component.libraries.length}");
}
if (component.libraries.length < 1) {
throw "No libraries were changed";
}
}
dir.deleteSync(recursive: true);
}
/// Apply all edits of a single iteration by updating the copy of the file in
/// the memory file system.
applyEdits(
List<Edit> edits,
OverlayFileSystem fs,
IncrementalKernelGenerator generator,
UriTranslator uriTranslator,
bool verbose) async {
for (var edit in edits) {
if (verbose) {
print('edit $edit');
}
var uri = edit.uri;
if (uri.scheme == 'package') uri = uriTranslator.translate(uri);
generator.invalidate(uri);
OverlayFileSystemEntity entity = fs.entityForUri(uri);
var contents = await entity.readAsString();
entity.writeAsStringSync(
contents.replaceAll(edit.original, edit.replacement));
}
}
/// Parse a set of edits from a JSON array. See library comment above for
/// details on the format.
List<ChangeSet> parse(List json) {
final changeSets = <ChangeSet>[];
for (final Map jsonChangeSet in json) {
final edits = <Edit>[];
for (final jsonEdit in jsonChangeSet['edits']) {
edits.add(new Edit(jsonEdit[0], jsonEdit[1], jsonEdit[2]));
}
changeSets.add(new ChangeSet(jsonChangeSet['name'], edits));
}
return changeSets;
}
/// An overlay file system that reads the original contents from the physical
/// file system, but performs updates to those files in memory.
///
/// All files in this file system use a custom URI of the form:
///
/// org-dartlang-overlay:///path/to/file.dart
///
/// This special scheme is mainly used to make it clear that the file belongs to
/// this file system and may not correspond to the contents on disk. However,
/// when the file is read for the first time, it will be retrieved from the
/// underlying file system by using the corresponding `file:*` URI:
///
/// file:///path/to/file.dart
class OverlayFileSystem implements FileSystem {
final MemoryFileSystem memory =
new MemoryFileSystem(Uri.parse('org-dartlang-overlay:///'));
final StandardFileSystem physical = StandardFileSystem.instance;
@override
FileSystemEntity entityForUri(Uri uri) {
if (uri.scheme == 'org-dartlang-overlay') {
return new OverlayFileSystemEntity(uri, this);
} else if (uri.scheme == 'file') {
// The IKG compiler reads ".packages" which might contain absolute file
// URIs (which it will then try to use on the FS). We therefore replace
// them with overlay-fs URIs as usual.
return new OverlayFileSystemEntity(_resolveOverlayUri('$uri'), this);
} else {
throw "Unsupported scheme: ${uri.scheme}."
" The OverlayFileSystem only accepts URIs"
" with the 'org-dartlang-overlay' scheme";
}
}
}
class OverlayFileSystemEntity implements FileSystemEntity {
final Uri uri;
FileSystemEntity _delegate;
final OverlayFileSystem _fs;
OverlayFileSystemEntity(this.uri, this._fs);
Future<FileSystemEntity> get delegate async {
if (_delegate != null) return _delegate;
FileSystemEntity entity = _fs.memory.entityForUri(uri);
if (await entity.exists()) {
_delegate = entity;
return _delegate;
}
return _delegate = _fs.physical.entityForUri(uri.replace(scheme: 'file'));
}
@override
Future<bool> exists() async => (await delegate).exists();
@override
Future<List<int>> readAsBytes() async => (await delegate).readAsBytes();
@override
Future<String> readAsString() async => (await delegate).readAsString();
void writeAsStringSync(String contents) =>
_fs.memory.entityForUri(uri).writeAsStringSync(contents);
}
ByteStore createByteStore(String cachePolicy, String path) {
switch (cachePolicy) {
case 'memory':
return new MemoryByteStore();
case 'protected':
return new ProtectedFileByteStore(path);
case 'evicting':
return new MemoryCachingByteStore(
new EvictingFileByteStore(path, 1024 * 1024 * 1024 /* 1G */),
64 * 1024 * 1024 /* 64M */);
default:
throw new UnsupportedError('Unknown cache policy: $cachePolicy');
}
}
/// A string replacement edit in a source file.
class Edit {
final Uri uri;
final String original;
final String replacement;
Edit(String uriString, this.original, this.replacement)
: uri = _resolveOverlayUri(uriString);
String toString() => 'Edit($uri, "$original" -> "$replacement")';
}
/// A named set of changes applied together.
class ChangeSet {
final String name;
final List<Edit> edits;
ChangeSet(this.name, this.edits);
String toString() => 'ChangeSet($name, $edits)';
}
_resolveOverlayUri(String uriString) {
Uri result = Uri.base.resolve(uriString);
return result.isScheme("file")
? result.replace(scheme: 'org-dartlang-overlay')
: result;
}
ArgParser argParser = new ArgParser()
..addFlag('verbose-compilation',
help: 'make the compiler verbose', defaultsTo: false)
..addFlag('verbose', help: 'print additional information', defaultsTo: false)
..addFlag('loop', help: 'run benchmark 8 times', defaultsTo: true)
..addOption('target',
help: 'target platform', defaultsTo: 'vm', allowed: ['vm', 'flutter'])
..addOption('cache',
help: 'caching policy used by the compiler',
defaultsTo: 'protected',
allowed: ['evicting', 'memory', 'protected'])
..addOption('mode',
help: 'whether to run in strong or legacy mode',
defaultsTo: 'strong',
allowed: ['legacy', 'strong'])
..addOption('implementation',
help: 'incremental compiler implementation to use',
defaultsTo: 'default',
allowed: ['default', 'minimal'])
..addOption('sdk-summary', help: 'Location of the sdk outline.dill file')
..addOption('sdk-library-specification',
help: 'Location of the '
'sdk/lib/libraries.json file');