[vm, bytecode] Generate bytecode per package in fronend_server incremental compiles.

flutter/examples/flutter_gallery$ flutter test --local-engine host_debug -v | grep took
              First run -> Second run (initialize from dill file)
AST             11539ms ->  6837ms
Bytecode before 32790ms -> 26665ms
Bytecode after  15435ms -> 11111ms

Change-Id: If1088f86a583170c9f3690778a5b8e2fd4b48ace
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118341
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Ryan Macnak 2019-09-26 21:36:45 +00:00 committed by commit-bot@chromium.org
parent 48f7636798
commit 4a69ac1344
2 changed files with 121 additions and 43 deletions

View file

@ -33,10 +33,12 @@ import 'package:vm/kernel_front_end.dart'
show
asFileUri,
compileToKernel,
parseCommandLineDefines,
convertFileOrUriArgumentToUri,
createFrontEndTarget,
createFrontEndFileSystem,
createFrontEndTarget,
forEachPackage,
packageFor,
parseCommandLineDefines,
runWithFrontEndCompilerContext,
setVMEnvironmentDefines,
writeDepfile;
@ -341,6 +343,7 @@ class FrontendCompiler implements CompilerInterface {
compilerOptions.bytecode = options['gen-bytecode'];
final BytecodeOptions bytecodeOptions = new BytecodeOptions(
enableAsserts: options['enable-asserts'],
emitSourceFiles: options['embed-source-text'],
environmentDefines: environmentDefines)
..parseCommandLineFlags(options['bytecode-options']);
@ -549,6 +552,59 @@ class FrontendCompiler implements CompilerInterface {
}
}
bool _elementsIdentical(List a, List b) {
if (a.length != b.length) return false;
for (int i = 0; i < a.length; i++) {
if (!identical(a[i], b[i])) return false;
}
return true;
}
final _packageLibraries = new Expando();
final _packageBytes = new Expando();
void _writePackage(Component component, String package,
List<Library> libraries, IOSink sink) {
final canCache = libraries.isNotEmpty &&
_compilerOptions.bytecode &&
errors.isEmpty &&
package != "main";
if (canCache) {
var cachedLibraries = _packageLibraries[libraries.first];
if ((cachedLibraries != null) &&
_elementsIdentical(cachedLibraries, libraries)) {
sink.add(_packageBytes[libraries.first]);
return;
}
}
Component partComponent = component;
if (_compilerOptions.bytecode && errors.isEmpty) {
generateBytecode(partComponent,
options: _bytecodeOptions,
libraries: libraries,
coreTypes: _generator.getCoreTypes(),
hierarchy: _generator.getClassHierarchy());
if (_options['drop-ast']) {
partComponent = createFreshComponentWithBytecode(partComponent);
}
}
final byteSink = new ByteSink();
final BinaryPrinter printer = new LimitedBinaryPrinter(byteSink,
(lib) => packageFor(lib) == package, false /* excludeUriToSource */);
printer.writeComponentFile(partComponent);
final bytes = byteSink.builder.takeBytes();
sink.add(bytes);
if (canCache) {
_packageLibraries[libraries.first] = libraries;
_packageBytes[libraries.first] = bytes;
}
}
@override
Future<Null> recompileDelta({String entryPoint}) async {
final String boundaryKey = new Uuid().generateV4();
@ -564,8 +620,21 @@ class FrontendCompiler implements CompilerInterface {
transformer.transform(deltaProgram);
}
final compiledSources = deltaProgram.uriToSource.keys;
deltaProgram = await _generateBytecodeIfNeeded(deltaProgram);
await writeDillFile(deltaProgram, _kernelBinaryFilename);
if (_compilerOptions.bytecode) {
final IOSink sink = new File(_kernelBinaryFilename).openWrite();
await runWithFrontEndCompilerContext(
_mainSource, _compilerOptions, deltaProgram, () async {
await forEachPackage(deltaProgram,
(String package, List<Library> libraries) async {
_writePackage(deltaProgram, package, libraries, sink);
});
});
await sink.close();
} else {
await writeDillFile(deltaProgram, _kernelBinaryFilename);
}
_outputStream.writeln(boundaryKey);
await _outputDependenciesDelta(compiledSources);
_outputStream

View file

@ -595,28 +595,6 @@ Future writeOutputSplitByPackages(
BytecodeOptions bytecodeOptions,
bool dropAST: false,
}) async {
// Package sharing: make the encoding not depend on the order in which parts
// of a package are loaded.
component.libraries.sort((Library a, Library b) {
return a.importUri.toString().compareTo(b.importUri.toString());
});
component.computeCanonicalNames();
for (Library lib in component.libraries) {
lib.additionalExports.sort((Reference a, Reference b) {
return a.canonicalName.toString().compareTo(b.canonicalName.toString());
});
}
final packagesSet = new Set<String>();
for (Library lib in component.libraries) {
packagesSet.add(packageFor(lib));
}
packagesSet.remove('main');
packagesSet.remove(null);
final List<String> packages = packagesSet.toList();
packages.add('main'); // Make sure main package is last.
if (bytecodeOptions.showBytecodeSizeStatistics) {
BytecodeSizeStatistics.reset();
}
@ -629,31 +607,24 @@ Future writeOutputSplitByPackages(
new ClassHierarchy(component, onAmbiguousSupertypes: (cls, a, b) {});
}
final packages = new List<String>();
await runWithFrontEndCompilerContext(source, compilerOptions, component,
() async {
for (String package in packages) {
await forEachPackage(component,
(String package, List<Library> libraries) async {
packages.add(package);
final String filename = '$outputFileName-$package.dilp';
final IOSink sink = new File(filename).openWrite();
final main = component.mainMethod;
final problems = component.problemsAsJson;
if (package != 'main') {
component.mainMethod = null;
component.problemsAsJson = null;
}
Component partComponent = component;
if (genBytecode) {
final List<Library> libraries = component.libraries
.where((lib) => packageFor(lib) == package)
.toList();
generateBytecode(component,
generateBytecode(partComponent,
options: bytecodeOptions,
libraries: libraries,
hierarchy: hierarchy);
if (dropAST) {
partComponent = createFreshComponentWithBytecode(component);
partComponent = createFreshComponentWithBytecode(partComponent);
}
}
@ -661,11 +632,8 @@ Future writeOutputSplitByPackages(
(lib) => packageFor(lib) == package, false /* excludeUriToSource */);
printer.writeComponentFile(partComponent);
component.mainMethod = main;
component.problemsAsJson = problems;
await sink.close();
}
});
});
if (bytecodeOptions.showBytecodeSizeStatistics) {
@ -692,6 +660,47 @@ String packageFor(Library lib) {
return 'main';
}
Future<Null> forEachPackage<T>(Component component,
T action(String package, List<Library> libraries)) async {
// Package sharing: make the encoding not depend on the order in which parts
// of a package are loaded.
component.libraries.sort((Library a, Library b) {
return a.importUri.toString().compareTo(b.importUri.toString());
});
component.computeCanonicalNames();
for (Library lib in component.libraries) {
lib.additionalExports.sort((Reference a, Reference b) {
return a.canonicalName.toString().compareTo(b.canonicalName.toString());
});
}
final packages = new Map<String, List<Library>>();
for (Library lib in component.libraries) {
packages.putIfAbsent(packageFor(lib), () => new List<Library>()).add(lib);
}
if (packages.containsKey(null)) {
packages.remove(null);
}
if (packages.containsKey('main')) {
// Make sure main package is last.
packages['main'] = packages.remove('main');
}
for (String package in packages.keys) {
final main = component.mainMethod;
final problems = component.problemsAsJson;
if (package != 'main') {
component.mainMethod = null;
component.problemsAsJson = null;
}
await action(package, packages[package]);
component.mainMethod = main;
component.problemsAsJson = problems;
}
}
String _escapePath(String path) {
return path.replaceAll('\\', '\\\\').replaceAll(' ', '\\ ');
}