Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 09b75a3

Browse files
rakudramacommit-bot@chromium.org
authored andcommitted
[benchmarks] Add benchmark to measure dart2js string pool cost
Change-Id: I15099af08526444c2e01da46b9939f2435547c90 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/201981 Commit-Queue: Stephen Adams <sra@google.com> Reviewed-by: Joshua Litt <joshualitt@google.com>
1 parent 2c22f0d commit 09b75a3

File tree

7 files changed

+5070
-0
lines changed

7 files changed

+5070
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright (c) 2021, 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 'package:benchmark_harness/benchmark_harness.dart';
6+
7+
import 'version1a.dart';
8+
import 'version1b.dart';
9+
import 'version2.dart';
10+
11+
// ## Organization
12+
//
13+
// version1a.dart is the same version1b.dart except for renaming of functions.
14+
//
15+
// Both contain the same ~1500 distinct large string literals. An application
16+
// will be smaller if compiled with sharing of the string constant values
17+
// between the corresponding functions in version1a.dart and version1b.dart.
18+
//
19+
// version2.dart has the same general pattern as version1{a,b}.dart, but with
20+
// unique string literals. As these literals have only one occurrence in the
21+
// program, they will not be pooled for access from multiple functions.
22+
//
23+
// StringPool100.dart is a separate benchmark program that, after tree-shaking,
24+
// has a 'small' string pool of ~100 strings rather than the ~1500 strings in
25+
// this file. This has to be a separate program since the string pool generated
26+
// by dart2js is for the whole-program (or whole deferred fragment).
27+
//
28+
// ## Interpretation
29+
//
30+
// Displayed results are normalized by the number of String literals accessed.
31+
//
32+
// StringPool.N.pooled uses N strings from the string pool.
33+
// StringPool.N.unpooled uses N strings without string pooling.
34+
//
35+
// Comparing StringPool.1500.{pooled,unpooled} gives an indication of the cost
36+
// of a large string pool.
37+
//
38+
// Comparing StringPool.100.{pooled,unpooled} gives an indication of the cost
39+
// of a small string pool.
40+
//
41+
// Comparing StringPool.{100,1500}.pooled gives an indication of the cost
42+
// of a large string pool compared to a small string pool.
43+
44+
const kStringLiteralsPerRun = 100000;
45+
46+
typedef Gen = List<String> Function(String);
47+
48+
abstract class StringPoolBase extends BenchmarkBase {
49+
StringPoolBase(String name) : super('StringPool.$name');
50+
51+
// A list of functions that generate a list of strings.
52+
List<Gen> get functions;
53+
54+
// The input list of generators is padded to a fixed length with one of the
55+
// generators.
56+
List<Gen> get _functions => __functions ?? complete(List.of(functions), 50);
57+
List<Gen>? __functions;
58+
59+
List<Gen> complete(List<Gen> list, int targetLength) {
60+
while (list.length != targetLength) {
61+
// The List is stretched using the same function so that one function is
62+
// similarly hot and potentially JIT-ed in the `.1500.` and `.100.`
63+
// benchmarks.
64+
list.add(list.first);
65+
}
66+
return list;
67+
}
68+
69+
@override
70+
void run() {
71+
int count = 0;
72+
LOOP:
73+
while (true) {
74+
for (final f in _functions) {
75+
final result = f(name);
76+
sink = result;
77+
count += result.length - 1; // First string is parameter
78+
if (count >= kStringLiteralsPerRun) break LOOP;
79+
}
80+
}
81+
}
82+
83+
@override
84+
void exercise() {
85+
// Run once instead of default 10 times since we do a lot of work in `run`.
86+
run();
87+
}
88+
}
89+
90+
class V1 extends StringPoolBase {
91+
V1() : super('1500.pooled');
92+
93+
@override
94+
late final functions = version1ax1500();
95+
}
96+
97+
class V1Copy extends StringPoolBase {
98+
V1Copy() : super('1500.pooled.copy');
99+
100+
@override
101+
late final functions = version1bx1500();
102+
}
103+
104+
class V2 extends StringPoolBase {
105+
V2() : super('1500.unpooled');
106+
107+
@override
108+
late final functions = version2x1500();
109+
}
110+
111+
dynamic sink;
112+
113+
void main() {
114+
// Compare results of V1 and V1Copy to ensure both classes and their reachable
115+
// functions are in the program.
116+
V1()
117+
..setup()
118+
..run()
119+
..run();
120+
final sink1a = sink;
121+
V1Copy()
122+
..setup()
123+
..run()
124+
..run();
125+
final sink1b = sink;
126+
if (sink1a.length != sink1b.length) throw StateError('Not same length');
127+
128+
V2()
129+
..setup()
130+
..run()
131+
..run();
132+
final sink2 = sink;
133+
if (sink1a.length != sink2.length) throw StateError('Not same length');
134+
135+
V1().report();
136+
V2().report();
137+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) 2021, 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+
// See `StringPool.dart` for comments.
6+
7+
import 'StringPool.dart' show StringPoolBase, sink;
8+
import 'version1a.dart';
9+
import 'version1b.dart';
10+
import 'version2.dart';
11+
12+
class V1 extends StringPoolBase {
13+
V1() : super('100.pooled');
14+
15+
@override
16+
late final functions = version1ax100();
17+
}
18+
19+
class V1Copy extends StringPoolBase {
20+
V1Copy() : super('100.pooled.copy');
21+
22+
@override
23+
late final functions = version1bx100();
24+
}
25+
26+
class V2 extends StringPoolBase {
27+
V2() : super('100.unpooled');
28+
29+
@override
30+
late final functions = version2x100();
31+
}
32+
33+
void main() {
34+
// Compare results of V1 and V1Copy to ensure both are in the program.
35+
V1()
36+
..setup()
37+
..run()
38+
..run();
39+
final sink1a = sink;
40+
V1Copy()
41+
..setup()
42+
..run()
43+
..run();
44+
final sink1b = sink;
45+
if (sink1a.length != sink1b.length) throw StateError('Not same length');
46+
47+
V2()
48+
..setup()
49+
..run()
50+
..run();
51+
final sink2 = sink;
52+
if (sink1a.length != sink2.length) throw StateError('Not same length');
53+
54+
V1().report();
55+
V2().report();
56+
}

0 commit comments

Comments
 (0)