Skip to content

Commit 86641d0

Browse files
alexmarkovcommit-bot@chromium.org
authored andcommitted
[vm, front-end server] Cache generated bytecode using separate file
Previously, generated bytecode was cached using metadata in kernel AST. When serializaing kernel AST (for subsequent compilations) incremental serialization was not used, so AST serialization was considerably slower in bytecode mode compared to non-bytecode mode. Apparently, metadata is not compatible with incremental serialization (#39302). This change reimplements caching of generated bytecode using a separate 'foo.dill.cache' file near the generated 'foo.dill' and 'foo.dill.ast'. AST is now written using incremental serialization both in bytecode and non-bytecode modes. flutter_test_performance benchmark: AST mode: "without_change_elapsed_time_ms": 2615-2652,   "implementation_change_elapsed_time_ms": 6292-6394,   "interface_change_elapsed_time_ms": 6183-6484,   "with_coverage_time_ms": 2723-2834 Bytecode mode, before this change: "without_change_elapsed_time_ms": 3246-3295,   "implementation_change_elapsed_time_ms": 7998-8068,   "interface_change_elapsed_time_ms": 7899-8029,   "with_coverage_time_ms": 3316-3378 Bytecode mode, after this change: "without_change_elapsed_time_ms": 2689-2737,   "implementation_change_elapsed_time_ms": 7630-7677,   "interface_change_elapsed_time_ms": 7724-7917,   "with_coverage_time_ms": 2841-2949 Change-Id: I603d33f49949146cf6620226d810f2f3cb9853a9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/124592 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
1 parent b5110a5 commit 86641d0

File tree

4 files changed

+137
-65
lines changed

4 files changed

+137
-65
lines changed

pkg/frontend_server/lib/frontend_server.dart

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ import 'package:kernel/target/targets.dart' show targets, TargetFlags;
2828
import 'package:path/path.dart' as path;
2929
import 'package:usage/uuid/uuid.dart';
3030

31-
import 'package:vm/metadata/binary_cache.dart'
32-
show BinaryCacheMetadataRepository;
33-
31+
import 'package:vm/binary_cache.dart' show BinaryCache, Range;
3432
import 'package:vm/bytecode/gen_bytecode.dart'
3533
show generateBytecode, createFreshComponentWithBytecode;
3634
import 'package:vm/bytecode/options.dart' show BytecodeOptions;
@@ -283,6 +281,8 @@ class FrontendCompiler implements CompilerInterface {
283281
String _kernelBinaryFilenameIncremental;
284282
String _kernelBinaryFilenameFull;
285283
String _initializeFromDill;
284+
String _bytecodeDillPath;
285+
String _bytecodeCachePath;
286286

287287
Set<Uri> previouslyReportedDependencies = Set<Uri>();
288288

@@ -404,12 +404,14 @@ class FrontendCompiler implements CompilerInterface {
404404
];
405405
}
406406

407-
if (compilerOptions.bytecode && _initializeFromDill != null) {
407+
_bytecodeCachePath = _initializeFromDill + ".cache";
408+
if (compilerOptions.bytecode) {
408409
// If we are generating bytecode, put bytecode only (not AST) in
409410
// [_kernelBinaryFilename], which the user of this tool will eventually
410411
// feed to Flutter engine or flutter_tester. Use a separate file to cache
411412
// the AST result to initialize the incremental compiler for the next
412413
// invocation of this tool.
414+
_bytecodeDillPath = _initializeFromDill;
413415
_initializeFromDill += ".ast";
414416
}
415417

@@ -556,25 +558,38 @@ class FrontendCompiler implements CompilerInterface {
556558
{bool filterExternal: false,
557559
IncrementalSerializer incrementalSerializer}) async {
558560
final Component component = results.component;
559-
// Remove the cache that came either from this function or from
560-
// initializing from a kernel file.
561-
component.metadata.remove(BinaryCacheMetadataRepository.repositoryTag);
562561

563562
if (_compilerOptions.bytecode) {
563+
BinaryCache inputCache;
564+
if (_options['incremental']) {
565+
inputCache = new BinaryCache();
566+
await inputCache.read(_bytecodeCachePath, _bytecodeDillPath);
567+
}
568+
BinaryCache outputCache = new BinaryCache();
569+
564570
{
565571
// Generate bytecode as the output proper.
566572
final IOSink sink = File(filename).openWrite();
573+
int offset = 0;
567574
await runWithFrontEndCompilerContext(
568575
_mainSource, _compilerOptions, component, () async {
569576
if (_options['incremental']) {
570577
// When loading a single kernel buffer with multiple sub-components,
571578
// the VM expects 'main' to be the first sub-component.
572579
await forEachPackage(component,
573580
(String package, List<Library> libraries) async {
574-
_writePackage(results, package, libraries, sink);
581+
offset = _writePackage(
582+
results,
583+
package,
584+
libraries,
585+
sink,
586+
offset,
587+
inputCache,
588+
outputCache,
589+
incrementalSerializer?.invalidatedUris);
575590
}, mainFirst: true);
576591
} else {
577-
_writePackage(results, 'main', component.libraries, sink);
592+
_writePackage(results, 'main', component.libraries, sink, 0);
578593
}
579594
});
580595
await sink.close();
@@ -585,15 +600,6 @@ class FrontendCompiler implements CompilerInterface {
585600
// of [filename] so that a later invocation of frontend_server will the
586601
// same arguments will use this to initialize its incremental kernel
587602
// compiler.
588-
final repository = BinaryCacheMetadataRepository();
589-
component.addMetadataRepository(repository);
590-
for (var lib in component.libraries) {
591-
var bytes = BinaryCacheMetadataRepository.lookup(lib);
592-
if (bytes != null) {
593-
repository.mapping[lib] = bytes;
594-
}
595-
}
596-
597603
final file = new File(_initializeFromDill);
598604
await file.create(recursive: true);
599605
final IOSink sink = file.openWrite();
@@ -607,9 +613,18 @@ class FrontendCompiler implements CompilerInterface {
607613

608614
sortComponent(component);
609615

616+
if (incrementalSerializer != null) {
617+
incrementalSerializer.writePackagesToSinkAndTrimComponent(
618+
component, sink);
619+
} else if (unsafePackageSerialization == true) {
620+
writePackagesToSinkAndTrimComponent(component, sink);
621+
}
622+
610623
printer.writeComponentFile(component);
611624
await sink.close();
612625
}
626+
627+
await outputCache.write(_bytecodeCachePath);
613628
} else {
614629
// Generate AST as the output proper.
615630
final IOSink sink = File(filename).openWrite();
@@ -632,6 +647,9 @@ class FrontendCompiler implements CompilerInterface {
632647

633648
printer.writeComponentFile(component);
634649
await sink.close();
650+
651+
// Reset cache if not generating bytecode.
652+
await new File(_bytecodeCachePath).writeAsString('');
635653
}
636654

637655
if (_options['split-output-by-packages']) {
@@ -714,18 +732,33 @@ class FrontendCompiler implements CompilerInterface {
714732
}
715733
}
716734

717-
void _writePackage(KernelCompilationResults result, String package,
718-
List<Library> libraries, IOSink sink) {
735+
int _writePackage(KernelCompilationResults result, String package,
736+
List<Library> libraries, IOSink sink, int offset,
737+
[BinaryCache inputCache,
738+
BinaryCache outputCache,
739+
Set<Uri> invalidatedUris]) {
719740
final canCache = libraries.isNotEmpty &&
720741
_compilerOptions.bytecode &&
721742
errors.isEmpty &&
722743
package != "main";
723744

724-
if (canCache) {
725-
var cachedBytes = BinaryCacheMetadataRepository.lookup(libraries.first);
726-
if (cachedBytes != null) {
727-
sink.add(cachedBytes);
728-
return;
745+
if (canCache && invalidatedUris != null) {
746+
final range = inputCache?.get(package);
747+
if (range != null) {
748+
bool invalidated = false;
749+
for (var lib in libraries) {
750+
if (invalidatedUris.contains(lib.fileUri)) {
751+
invalidated = true;
752+
break;
753+
}
754+
}
755+
756+
if (!invalidated) {
757+
final cachedBytes = inputCache.getBytes(range);
758+
sink.add(cachedBytes);
759+
outputCache.add(package, Range(offset, cachedBytes.length));
760+
return offset + cachedBytes.length;
761+
}
729762
}
730763
}
731764

@@ -750,8 +783,9 @@ class FrontendCompiler implements CompilerInterface {
750783
final bytes = byteSink.builder.takeBytes();
751784
sink.add(bytes);
752785
if (canCache) {
753-
BinaryCacheMetadataRepository.insert(libraries.first, bytes);
786+
outputCache.add(package, Range(offset, bytes.length));
754787
}
788+
return offset + bytes.length;
755789
}
756790

757791
@override

pkg/vm/lib/binary_cache.dart

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
library vm.binary_cache;
6+
7+
import 'dart:convert' show jsonEncode, jsonDecode;
8+
import 'dart:io' show File, IOException;
9+
import 'dart:typed_data' show ByteBuffer, Uint8List;
10+
11+
class Range {
12+
final int offset;
13+
final int length;
14+
15+
Range(this.offset, this.length);
16+
17+
@override
18+
String toString() => '[$offset, ${offset + length}]';
19+
}
20+
21+
class BinaryCache {
22+
// Maps package name to binary data.
23+
final _cache = <String, Range>{};
24+
ByteBuffer data;
25+
26+
Future<void> write(String descriptorPath) async {
27+
final entries = [];
28+
for (var package in _cache.keys) {
29+
final range = _cache[package];
30+
entries.add({
31+
'package': package,
32+
'offset': '${range.offset}',
33+
'length': '${range.length}'
34+
});
35+
}
36+
final String json = jsonEncode(entries);
37+
return new File(descriptorPath).writeAsString(json);
38+
}
39+
40+
void read(String descriptorPath, String binaryPath) async {
41+
try {
42+
final entries = jsonDecode(await new File(descriptorPath).readAsString());
43+
for (var e in entries) {
44+
_cache[e['package']] =
45+
new Range(int.parse(e['offset']), int.parse(e['length']));
46+
}
47+
} on IOException {
48+
_cache.clear();
49+
}
50+
if (_cache.isEmpty) {
51+
return;
52+
}
53+
try {
54+
data = (await new File(binaryPath).readAsBytes()).buffer;
55+
} on IOException {
56+
_cache.clear();
57+
}
58+
}
59+
60+
void invalidate(String package) {
61+
_cache.remove(package);
62+
}
63+
64+
Range get(String package) => _cache[package];
65+
66+
Uint8List getBytes(Range range) =>
67+
new Uint8List.view(data, range.offset, range.length);
68+
69+
bool get isEmpty => _cache.isEmpty;
70+
71+
void add(String package, Range data) {
72+
_cache[package] = data;
73+
}
74+
75+
@override
76+
String toString() => _cache.toString();
77+
}

pkg/vm/lib/metadata/binary_cache.dart

Lines changed: 0 additions & 37 deletions
This file was deleted.

pkg/vm/lib/target/vm.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import 'package:kernel/transformations/continuation.dart' as transformAsync
1717
import 'package:kernel/vm/constants_native_effects.dart'
1818
show VmConstantsBackend;
1919

20-
import '../metadata/binary_cache.dart' show BinaryCacheMetadataRepository;
2120
import '../transformations/call_site_annotator.dart' as callSiteAnnotator;
2221
import '../transformations/list_factory_specializer.dart'
2322
as listFactorySpecializer;
@@ -370,7 +369,6 @@ class VmTarget extends Target {
370369
@override
371370
Component configureComponent(Component component) {
372371
callSiteAnnotator.addRepositoryTo(component);
373-
component.addMetadataRepository(new BinaryCacheMetadataRepository());
374372
return super.configureComponent(component);
375373
}
376374

0 commit comments

Comments
 (0)