Skip to content

Commit 0bf8d38

Browse files
rmacnak-googlecommit-bot@chromium.org
authored andcommitted
[vm] Defer object pools.
This is a step toward deferring literals, to effectively split binary size for libraries that are more data than code, such as localizations. Bare instructions mode still has all constants in the root snapshot because it has one global ObjectPool. dart2js+analyzer product X64 non-bare: app.so 10072728 -> 8479824 (-15.8%) app.so-2.part.so 14074512 -> 15016976 (6.69%) app.so-3.part.so 9225936 -> 9888984 (7.18%) TEST=ci Bug: #41974 Change-Id: If61f6156e3fc2dd539331335b3bf0458102b7c75 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/168991 Commit-Queue: Ryan Macnak <[email protected]> Reviewed-by: Régis Crelier <[email protected]>
1 parent fb5ffc8 commit 0bf8d38

12 files changed

+708
-263
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2020, 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+
import "split_literals_deferred.dart" deferred as lib;
6+
7+
class Box {
8+
final contents;
9+
const Box(this.contents);
10+
String toString() => "Box($contents)";
11+
}
12+
13+
main() async {
14+
print("Root literal!");
15+
print(const <String>["Root literal in a list!"]);
16+
print(const <String, String>{"key": "Root literal in a map!"});
17+
print(const Box("Root literal in a box!"));
18+
19+
await lib.loadLibrary();
20+
lib.foo();
21+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) 2020, 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+
import "split_literals.dart";
6+
7+
void foo() {
8+
print("Deferred literal!");
9+
print(const <String>["Deferred literal in a list!"]);
10+
print(const <String, String>{"key": "Deferred literal in a map!"});
11+
print(const Box("Deferred literal in a box!"));
12+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) 2020, 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+
import "dart:convert";
6+
import "dart:io";
7+
8+
import "package:expect/expect.dart";
9+
import "package:path/path.dart" as path;
10+
11+
import "use_flag_test_helper.dart";
12+
13+
main(List<String> args) async {
14+
if (!isAOTRuntime) {
15+
return; // Running in JIT: AOT binaries not available.
16+
}
17+
18+
if (Platform.isAndroid) {
19+
return; // SDK tree not available on the test device.
20+
}
21+
22+
// These are the tools we need to be available to run on a given platform:
23+
if (!File(platformDill).existsSync()) {
24+
throw "Cannot run test as $platformDill does not exist";
25+
}
26+
if (!await testExecutable(genSnapshot)) {
27+
throw "Cannot run test as $genSnapshot not available";
28+
}
29+
30+
sanitizedPartitioning(manifest) {
31+
// Filter core libraries, relativize URIs, and sort to make the results less
32+
// sensitive to compiler or test harness changes.
33+
print(manifest);
34+
var units = <List<String>>[];
35+
for (var unit in manifest['loadingUnits']) {
36+
var uris = <String>[];
37+
for (var uri in unit['libraries']) {
38+
if (uri.startsWith("dart:")) continue;
39+
uris.add(Uri.parse(uri).pathSegments.last);
40+
}
41+
uris.sort((a, b) => a.compareTo(b));
42+
units.add(uris);
43+
}
44+
units.sort((a, b) => a.first.compareTo(b.first));
45+
print(units);
46+
return units;
47+
}
48+
49+
await withTempDir("split-literals-test", (String tempDir) async {
50+
final source =
51+
path.join(sdkDir, "runtime/tests/vm/dart_2/split_literals.dart");
52+
final dill = path.join(tempDir, "split_literals.dart.dill");
53+
final snapshot = path.join(tempDir, "split_literals.so");
54+
final manifest = path.join(tempDir, "split_literals.txt");
55+
final deferredSnapshot = snapshot + "-2.part.so";
56+
57+
// Compile source to kernel.
58+
await run(genKernel, <String>[
59+
"--aot",
60+
"--platform=$platformDill",
61+
"-o",
62+
dill,
63+
source,
64+
]);
65+
66+
// Compile kernel to ELF.
67+
await run(genSnapshot, <String>[
68+
"--use_bare_instructions=false",
69+
"--snapshot-kind=app-aot-elf",
70+
"--elf=$snapshot",
71+
"--loading-unit-manifest=$manifest",
72+
dill,
73+
]);
74+
var manifestContent = jsonDecode(await new File(manifest).readAsString());
75+
Expect.equals(2, manifestContent["loadingUnits"].length);
76+
// Note package:expect doesn't do deep equals on collections.
77+
Expect.equals(
78+
"[[split_literals.dart],"
79+
" [split_literals_deferred.dart]]",
80+
sanitizedPartitioning(manifestContent).toString());
81+
Expect.isTrue(await new File(deferredSnapshot).exists());
82+
83+
bool containsSubsequence(haystack, needle) {
84+
outer:
85+
for (var i = 0, n = haystack.length - needle.length; i < n; i++) {
86+
for (var j = 0; j < needle.length; j++) {
87+
if (haystack[i + j] != needle.codeUnitAt(j)) continue outer;
88+
}
89+
return true;
90+
}
91+
return false;
92+
}
93+
94+
var unit_1 = await new File(snapshot).readAsBytes();
95+
Expect.isTrue(containsSubsequence(unit_1, "Root literal!"));
96+
Expect.isTrue(containsSubsequence(unit_1, "Root literal in a list!"));
97+
Expect.isTrue(containsSubsequence(unit_1, "Root literal in a map!"));
98+
Expect.isTrue(containsSubsequence(unit_1, "Root literal in a box!"));
99+
Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal!"));
100+
Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a list!"));
101+
Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a map!"));
102+
Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a box!"));
103+
104+
var unit_2 = await new File(deferredSnapshot).readAsBytes();
105+
Expect.isTrue(!containsSubsequence(unit_2, "Root literal!"));
106+
Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a list!"));
107+
Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a map!"));
108+
Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a box!"));
109+
Expect.isTrue(containsSubsequence(unit_2, "Deferred literal!"));
110+
Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a list!"));
111+
Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a map!"));
112+
Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a box!"));
113+
});
114+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2020, 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+
import "split_literals_deferred.dart" deferred as lib;
6+
7+
class Box {
8+
final contents;
9+
const Box(this.contents);
10+
String toString() => "Box($contents)";
11+
}
12+
13+
main() async {
14+
print("Root literal!");
15+
print(const <String>["Root literal in a list!"]);
16+
print(const <String, String>{"key": "Root literal in a map!"});
17+
print(const Box("Root literal in a box!"));
18+
19+
await lib.loadLibrary();
20+
lib.foo();
21+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) 2020, 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+
import "split_literals.dart";
6+
7+
void foo() {
8+
print("Deferred literal!");
9+
print(const <String>["Deferred literal in a list!"]);
10+
print(const <String, String>{"key": "Deferred literal in a map!"});
11+
print(const Box("Deferred literal in a box!"));
12+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) 2020, 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+
import "dart:convert";
6+
import "dart:io";
7+
8+
import "package:expect/expect.dart";
9+
import "package:path/path.dart" as path;
10+
11+
import "use_flag_test_helper.dart";
12+
13+
main(List<String> args) async {
14+
if (!isAOTRuntime) {
15+
return; // Running in JIT: AOT binaries not available.
16+
}
17+
18+
if (Platform.isAndroid) {
19+
return; // SDK tree not available on the test device.
20+
}
21+
22+
// These are the tools we need to be available to run on a given platform:
23+
if (!File(platformDill).existsSync()) {
24+
throw "Cannot run test as $platformDill does not exist";
25+
}
26+
if (!await testExecutable(genSnapshot)) {
27+
throw "Cannot run test as $genSnapshot not available";
28+
}
29+
30+
sanitizedPartitioning(manifest) {
31+
// Filter core libraries, relativize URIs, and sort to make the results less
32+
// sensitive to compiler or test harness changes.
33+
print(manifest);
34+
var units = <List<String>>[];
35+
for (var unit in manifest['loadingUnits']) {
36+
var uris = <String>[];
37+
for (var uri in unit['libraries']) {
38+
if (uri.startsWith("dart:")) continue;
39+
uris.add(Uri.parse(uri).pathSegments.last);
40+
}
41+
uris.sort((a, b) => a.compareTo(b));
42+
units.add(uris);
43+
}
44+
units.sort((a, b) => a.first.compareTo(b.first));
45+
print(units);
46+
return units;
47+
}
48+
49+
await withTempDir("split-literals-test", (String tempDir) async {
50+
final source =
51+
path.join(sdkDir, "runtime/tests/vm/dart_2/split_literals.dart");
52+
final dill = path.join(tempDir, "split_literals.dart.dill");
53+
final snapshot = path.join(tempDir, "split_literals.so");
54+
final manifest = path.join(tempDir, "split_literals.txt");
55+
final deferredSnapshot = snapshot + "-2.part.so";
56+
57+
// Compile source to kernel.
58+
await run(genKernel, <String>[
59+
"--aot",
60+
"--platform=$platformDill",
61+
"-o",
62+
dill,
63+
source,
64+
]);
65+
66+
// Compile kernel to ELF.
67+
await run(genSnapshot, <String>[
68+
"--use_bare_instructions=false",
69+
"--snapshot-kind=app-aot-elf",
70+
"--elf=$snapshot",
71+
"--loading-unit-manifest=$manifest",
72+
dill,
73+
]);
74+
var manifestContent = jsonDecode(await new File(manifest).readAsString());
75+
Expect.equals(2, manifestContent["loadingUnits"].length);
76+
// Note package:expect doesn't do deep equals on collections.
77+
Expect.equals(
78+
"[[split_literals.dart],"
79+
" [split_literals_deferred.dart]]",
80+
sanitizedPartitioning(manifestContent).toString());
81+
Expect.isTrue(await new File(deferredSnapshot).exists());
82+
83+
bool containsSubsequence(haystack, needle) {
84+
outer:
85+
for (var i = 0, n = haystack.length - needle.length; i < n; i++) {
86+
for (var j = 0; j < needle.length; j++) {
87+
if (haystack[i + j] != needle.codeUnitAt(j)) continue outer;
88+
}
89+
return true;
90+
}
91+
return false;
92+
}
93+
94+
var unit_1 = await new File(snapshot).readAsBytes();
95+
Expect.isTrue(containsSubsequence(unit_1, "Root literal!"));
96+
Expect.isTrue(containsSubsequence(unit_1, "Root literal in a list!"));
97+
Expect.isTrue(containsSubsequence(unit_1, "Root literal in a map!"));
98+
Expect.isTrue(containsSubsequence(unit_1, "Root literal in a box!"));
99+
Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal!"));
100+
Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a list!"));
101+
Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a map!"));
102+
Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a box!"));
103+
104+
var unit_2 = await new File(deferredSnapshot).readAsBytes();
105+
Expect.isTrue(!containsSubsequence(unit_2, "Root literal!"));
106+
Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a list!"));
107+
Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a map!"));
108+
Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a box!"));
109+
Expect.isTrue(containsSubsequence(unit_2, "Deferred literal!"));
110+
Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a list!"));
111+
Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a map!"));
112+
Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a box!"));
113+
});
114+
}

0 commit comments

Comments
 (0)