Skip to content

Commit b55dc76

Browse files
alexmarkovCommit Bot
authored and
Commit Bot
committed
[benchmarks] Add micro-benchmark for async/await with live variables
Issue: #48594 Issue: #48378 Change-Id: Ib5bd65c149342bb5d9bb2b5176a4a3b968a08a81 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/238980 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Alexander Markov <[email protected]>
1 parent 5234076 commit b55dc76

File tree

4 files changed

+748
-0
lines changed

4 files changed

+748
-0
lines changed
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
// Copyright (c) 2022, 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+
// Micro-benchmark for testing async/await performance in presence of
6+
// different number of live values across await.
7+
8+
import 'dart:async';
9+
10+
import 'async_benchmark_base.dart' show AsyncBenchmarkBase;
11+
12+
class MockClass {
13+
static final String str = "${int.parse('42')}";
14+
static final List<int> list =
15+
List<int>.filled(int.parse('3'), int.parse('42'));
16+
17+
@pragma('vm:never-inline')
18+
@pragma('dart2js:noInline')
19+
String get1() => str;
20+
21+
@pragma('vm:never-inline')
22+
@pragma('dart2js:noInline')
23+
List<int> get2() => list;
24+
25+
@pragma('vm:never-inline')
26+
@pragma('dart2js:noInline')
27+
void use1(String a0) => a0.length;
28+
29+
@pragma('vm:never-inline')
30+
@pragma('dart2js:noInline')
31+
void use2(String a0, List<int> a1) => a0.length + a1.length;
32+
33+
@pragma('vm:never-inline')
34+
@pragma('dart2js:noInline')
35+
void use4(String a0, List<int> a1, String a2, List<int> a3) =>
36+
a0.length + a1.length + a2.length + a3.length;
37+
38+
@pragma('vm:never-inline')
39+
@pragma('dart2js:noInline')
40+
void use8(String a0, List<int> a1, String a2, List<int> a3, String a4,
41+
List<int> a5, String a6, List<int> a7) =>
42+
a0.length +
43+
a1.length +
44+
a2.length +
45+
a3.length +
46+
a4.length +
47+
a5.length +
48+
a6.length +
49+
a7.length;
50+
51+
@pragma('vm:never-inline')
52+
@pragma('dart2js:noInline')
53+
Future<void> asyncMethod() async {}
54+
}
55+
56+
class MockClass2 {
57+
static int val1 = int.parse('42');
58+
static int val2 = int.parse('43');
59+
60+
@pragma('vm:never-inline')
61+
@pragma('dart2js:noInline')
62+
int get1() => val1;
63+
64+
@pragma('vm:never-inline')
65+
@pragma('dart2js:noInline')
66+
int get2() => val2;
67+
68+
@pragma('vm:never-inline')
69+
@pragma('dart2js:noInline')
70+
void use1(int a0) => a0;
71+
72+
@pragma('vm:never-inline')
73+
@pragma('dart2js:noInline')
74+
void use2(int a0, int a1) => a0 + a1;
75+
76+
@pragma('vm:never-inline')
77+
@pragma('dart2js:noInline')
78+
void use4(int a0, int a1, int a2, int a3) => a0 + a1 + a2 + a3;
79+
80+
@pragma('vm:never-inline')
81+
@pragma('dart2js:noInline')
82+
Future<void> asyncMethod() async {}
83+
}
84+
85+
class LiveVarsBench extends AsyncBenchmarkBase {
86+
LiveVarsBench(String name) : super(name);
87+
@override
88+
Future<void> exercise() async {
89+
// These micro-benchmarks are too small, so
90+
// make a larger number of iterations per measurement.
91+
for (var i = 0; i < 10000; i++) {
92+
await run();
93+
}
94+
}
95+
}
96+
97+
class LiveObj1 extends LiveVarsBench {
98+
LiveObj1() : super('AsyncLiveVars.LiveObj1');
99+
final field1 = MockClass();
100+
@override
101+
Future<void> run() async {
102+
final obj1 = field1.get1();
103+
await field1.asyncMethod();
104+
field1.use1(obj1);
105+
await field1.asyncMethod();
106+
field1.use1(obj1);
107+
await field1.asyncMethod();
108+
field1.use1(obj1);
109+
}
110+
}
111+
112+
class LiveObj2 extends LiveVarsBench {
113+
LiveObj2() : super('AsyncLiveVars.LiveObj2');
114+
final field1 = MockClass();
115+
@override
116+
Future<void> run() async {
117+
final obj1 = field1.get1();
118+
final obj2 = field1.get2();
119+
await field1.asyncMethod();
120+
field1.use1(obj1);
121+
await field1.asyncMethod();
122+
field1.use1(obj1);
123+
await field1.asyncMethod();
124+
field1.use2(obj1, obj2);
125+
}
126+
}
127+
128+
class LiveObj4 extends LiveVarsBench {
129+
LiveObj4() : super('AsyncLiveVars.LiveObj4');
130+
final field1 = MockClass();
131+
final field2 = MockClass();
132+
@override
133+
Future<void> run() async {
134+
final obj1 = field1.get1();
135+
final obj2 = field1.get2();
136+
final obj3 = field2.get1();
137+
final obj4 = field2.get2();
138+
await field1.asyncMethod();
139+
field1.use1(obj1);
140+
await field1.asyncMethod();
141+
field2.use1(obj3);
142+
await field2.asyncMethod();
143+
field1.use4(obj1, obj2, obj3, obj4);
144+
}
145+
}
146+
147+
class LiveObj8 extends LiveVarsBench {
148+
LiveObj8() : super('AsyncLiveVars.LiveObj8');
149+
final field1 = MockClass();
150+
final field2 = MockClass();
151+
final field3 = MockClass();
152+
final field4 = MockClass();
153+
@override
154+
Future<void> run() async {
155+
final obj1 = field1.get1();
156+
final obj2 = field1.get2();
157+
final obj3 = field2.get1();
158+
final obj4 = field2.get2();
159+
final obj5 = field3.get1();
160+
final obj6 = field3.get2();
161+
final obj7 = field4.get1();
162+
final obj8 = field4.get2();
163+
await field1.asyncMethod();
164+
field1.use1(obj1);
165+
await field2.asyncMethod();
166+
field3.use2(obj5, obj6);
167+
await field4.asyncMethod();
168+
field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
169+
}
170+
}
171+
172+
class LiveObj16 extends LiveVarsBench {
173+
LiveObj16() : super('AsyncLiveVars.LiveObj16');
174+
final field1 = MockClass();
175+
final field2 = MockClass();
176+
final field3 = MockClass();
177+
final field4 = MockClass();
178+
final field5 = MockClass();
179+
final field6 = MockClass();
180+
final field7 = MockClass();
181+
final field8 = MockClass();
182+
@override
183+
Future<void> run() async {
184+
final obj1 = field1.get1();
185+
final obj2 = field1.get2();
186+
final obj3 = field2.get1();
187+
final obj4 = field2.get2();
188+
final obj5 = field3.get1();
189+
final obj6 = field3.get2();
190+
final obj7 = field4.get1();
191+
final obj8 = field4.get2();
192+
final obj9 = field5.get1();
193+
final obj10 = field5.get2();
194+
final obj11 = field6.get1();
195+
final obj12 = field6.get2();
196+
final obj13 = field7.get1();
197+
final obj14 = field7.get2();
198+
final obj15 = field8.get1();
199+
final obj16 = field8.get2();
200+
await field1.asyncMethod();
201+
field1.use1(obj1);
202+
await field2.asyncMethod();
203+
field5.use2(obj11, obj12);
204+
await field4.asyncMethod();
205+
field2.use8(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
206+
field3.use8(obj9, obj10, obj11, obj12, obj13, obj14, obj15, obj16);
207+
}
208+
}
209+
210+
class LiveInt1 extends LiveVarsBench {
211+
LiveInt1() : super('AsyncLiveVars.LiveInt1');
212+
final field1 = MockClass2();
213+
@override
214+
Future<void> run() async {
215+
final int1 = field1.get1();
216+
await field1.asyncMethod();
217+
field1.use1(int1);
218+
await field1.asyncMethod();
219+
field1.use1(int1);
220+
await field1.asyncMethod();
221+
field1.use1(int1);
222+
}
223+
}
224+
225+
class LiveInt4 extends LiveVarsBench {
226+
LiveInt4() : super('AsyncLiveVars.LiveInt4');
227+
final field1 = MockClass2();
228+
final field2 = MockClass2();
229+
@override
230+
Future<void> run() async {
231+
final int1 = field1.get1();
232+
final int2 = field1.get2();
233+
final int3 = field2.get1();
234+
final int4 = field2.get2();
235+
await field1.asyncMethod();
236+
field1.use1(int1);
237+
await field1.asyncMethod();
238+
field2.use1(int3);
239+
await field2.asyncMethod();
240+
field1.use4(int1, int2, int3, int4);
241+
}
242+
}
243+
244+
class LiveObj2Int2 extends LiveVarsBench {
245+
LiveObj2Int2() : super('AsyncLiveVars.LiveObj2Int2');
246+
final field1 = MockClass();
247+
final field2 = MockClass2();
248+
@override
249+
Future<void> run() async {
250+
final obj1 = field1.get1();
251+
final obj2 = field1.get2();
252+
final int1 = field2.get1();
253+
final int2 = field2.get2();
254+
await field1.asyncMethod();
255+
field1.use1(obj1);
256+
await field1.asyncMethod();
257+
field2.use1(int1);
258+
await field2.asyncMethod();
259+
field1.use2(obj1, obj2);
260+
field2.use2(int1, int2);
261+
}
262+
}
263+
264+
class LiveObj4Int4 extends LiveVarsBench {
265+
LiveObj4Int4() : super('AsyncLiveVars.LiveObj4Int4');
266+
final field1 = MockClass();
267+
final field2 = MockClass();
268+
final field3 = MockClass2();
269+
final field4 = MockClass2();
270+
@override
271+
Future<void> run() async {
272+
final obj1 = field1.get1();
273+
final obj2 = field1.get2();
274+
final obj3 = field2.get1();
275+
final obj4 = field2.get2();
276+
final int1 = field3.get1();
277+
final int2 = field3.get2();
278+
final int3 = field4.get1();
279+
final int4 = field4.get2();
280+
await field1.asyncMethod();
281+
field1.use1(obj1);
282+
await field2.asyncMethod();
283+
field3.use2(int2, int4);
284+
await field4.asyncMethod();
285+
field2.use4(obj1, obj2, obj3, obj4);
286+
field4.use4(int1, int2, int3, int4);
287+
}
288+
}
289+
290+
Future<void> main() async {
291+
final benchmarks = [
292+
LiveObj1(),
293+
LiveObj2(),
294+
LiveObj4(),
295+
LiveObj8(),
296+
LiveObj16(),
297+
LiveInt1(),
298+
LiveInt4(),
299+
LiveObj2Int2(),
300+
LiveObj4Int4()
301+
];
302+
for (final bench in benchmarks) {
303+
await bench.report();
304+
}
305+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) 2022, 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+
show PrintEmitter, ScoreEmitter;
7+
8+
// Similar to BenchmarkBase from package:benchmark_harness.
9+
class AsyncBenchmarkBase {
10+
final String name;
11+
final ScoreEmitter emitter;
12+
13+
// Empty constructor.
14+
const AsyncBenchmarkBase(this.name, {this.emitter = const PrintEmitter()});
15+
16+
// The benchmark code.
17+
// This function is not used, if both [warmup] and [exercise] are overwritten.
18+
Future<void> run() async {}
19+
20+
// Runs a short version of the benchmark. By default invokes [run] once.
21+
Future<void> warmup() async {
22+
await run();
23+
}
24+
25+
// Exercises the benchmark. By default invokes [run] 10 times.
26+
Future<void> exercise() async {
27+
for (var i = 0; i < 10; i++) {
28+
await run();
29+
}
30+
}
31+
32+
// Not measured setup code executed prior to the benchmark runs.
33+
Future<void> setup() async {}
34+
35+
// Not measures teardown code executed after the benchark runs.
36+
Future<void> teardown() async {}
37+
38+
// Measures the score for this benchmark by executing it repeatedly until
39+
// time minimum has been reached.
40+
static Future<double> measureFor(Function f, int minimumMillis) async {
41+
final int minimumMicros = minimumMillis * 1000;
42+
int iter = 0;
43+
final watch = Stopwatch();
44+
watch.start();
45+
int elapsed = 0;
46+
while (elapsed < minimumMicros) {
47+
await f();
48+
elapsed = watch.elapsedMicroseconds;
49+
iter++;
50+
}
51+
return elapsed / iter;
52+
}
53+
54+
// Measures the score for the benchmark and returns it.
55+
Future<double> measure() async {
56+
await setup();
57+
// Warmup for at least 100ms. Discard result.
58+
await measureFor(warmup, 100);
59+
// Run the benchmark for at least 2000ms.
60+
final result = await measureFor(exercise, 2000);
61+
await teardown();
62+
return result;
63+
}
64+
65+
Future<void> report() async {
66+
emitter.emit(name, await measure());
67+
}
68+
}

0 commit comments

Comments
 (0)