Skip to content

Commit 063f59f

Browse files
authored
Reland Add UI Benchmarks (#143542) (#143799)
Re-lands flutter/flutter#143542 (which is part 1 of flutter/flutter#138481) The filename was wrong ð�«
1 parent 41581c9 commit 063f59f

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/animation.dart';
6+
import 'package:flutter/foundation.dart';
7+
import 'package:flutter_test/flutter_test.dart';
8+
9+
import '../common.dart';
10+
11+
const int _kNumIterationsList = 2 << 14;
12+
const int _kNumIterationsHashed = 2 << 19;
13+
const int _kNumWarmUp = 2 << 6;
14+
const List<int> callbackCounts = <int>[1, 10, 100, 500];
15+
16+
class TestAnimationController extends AnimationController {
17+
TestAnimationController() : super(vsync: const TestVSync());
18+
19+
@override
20+
void notifyListeners() => super.notifyListeners();
21+
}
22+
23+
void main() {
24+
assert(false,
25+
"Don't run benchmarks in debug mode! Use 'flutter run --release'.");
26+
27+
final BenchmarkResultPrinter printer = BenchmarkResultPrinter();
28+
29+
void runNotifiyListenersLoopWithObserverList(
30+
int totalIterations, {
31+
bool failRemoval = false,
32+
bool addResult = true,
33+
}) {
34+
final String suffix = failRemoval ? 'removalFail' : 'removalSuccess';
35+
final String name = 'notifyListeners:ObserverList:$suffix';
36+
37+
void miss() {}
38+
39+
for (final int callbackCount in callbackCounts) {
40+
final int iterations = totalIterations ~/ callbackCount;
41+
42+
final ObserverList<VoidCallback> observerList =
43+
ObserverList<VoidCallback>();
44+
for (int i = 0; i < callbackCount; ++i) {
45+
observerList.add(
46+
switch (failRemoval) {
47+
false => () {
48+
final VoidCallback first =
49+
(observerList.iterator..moveNext()).current;
50+
51+
observerList.remove(first);
52+
observerList.add(first);
53+
},
54+
true => () => observerList.remove(miss),
55+
},
56+
);
57+
}
58+
59+
final Stopwatch watch = Stopwatch()..start();
60+
61+
for (int i = 0; i < iterations; ++i) {
62+
final List<VoidCallback> list = observerList.toList(growable: false);
63+
for (final VoidCallback cb in list) {
64+
if (observerList.contains(cb)) {
65+
cb();
66+
}
67+
}
68+
}
69+
70+
watch.stop();
71+
72+
if (addResult) {
73+
printer.addResult(
74+
description: '$name ($callbackCount callbacks)',
75+
value: watch.elapsedMicroseconds / iterations,
76+
unit: 'µs per iteration',
77+
name: '$name$callbackCount',
78+
);
79+
}
80+
}
81+
}
82+
83+
void runNotifiyListenersLoopWithHashedObserverList(
84+
int totalIterations, {
85+
bool addResult = true,
86+
}) {
87+
const String name = 'notifyListeners:HashedObserverList';
88+
89+
for (final int callbackCount in callbackCounts) {
90+
final int iterations = totalIterations ~/ callbackCount;
91+
92+
final HashedObserverList<VoidCallback> observerList =
93+
HashedObserverList<VoidCallback>();
94+
for (int i = 0; i < callbackCount; ++i) {
95+
observerList.add(() {
96+
final VoidCallback first =
97+
(observerList.iterator..moveNext()).current;
98+
99+
observerList.remove(first);
100+
observerList.add(first);
101+
});
102+
}
103+
104+
final Stopwatch watch = Stopwatch()..start();
105+
106+
for (int i = 0; i < iterations; ++i) {
107+
final List<VoidCallback> list = observerList.toList(growable: false);
108+
for (final VoidCallback cb in list) {
109+
if (observerList.contains(cb)) {
110+
cb();
111+
}
112+
}
113+
}
114+
115+
watch.stop();
116+
117+
if (addResult) {
118+
printer.addResult(
119+
description: '$name ($callbackCount callbacks)',
120+
value: watch.elapsedMicroseconds / iterations,
121+
unit: 'µs per iteration',
122+
name: '$name$callbackCount',
123+
);
124+
}
125+
}
126+
}
127+
128+
void runNotifiyListenersLoopWithAnimationController(
129+
int totalIterations, {
130+
bool addResult = true,
131+
}) {
132+
const String name = 'notifyListeners:AnimationController';
133+
134+
for (final int callbackCount in callbackCounts) {
135+
final int iterations = totalIterations ~/ callbackCount;
136+
137+
final TestAnimationController controller = TestAnimationController();
138+
for (int i = 0; i < callbackCount; ++i) {
139+
late final VoidCallback cb;
140+
cb = () {
141+
controller.removeListener(cb);
142+
controller.addListener(cb);
143+
};
144+
controller.addListener(cb);
145+
}
146+
147+
final Stopwatch watch = Stopwatch()..start();
148+
149+
for (int i = 0; i < iterations; ++i) {
150+
controller.notifyListeners();
151+
}
152+
153+
watch.stop();
154+
155+
if (addResult) {
156+
printer.addResult(
157+
description: '$name ($callbackCount callbacks)',
158+
value: watch.elapsedMicroseconds / iterations,
159+
unit: 'µs per iteration',
160+
name: '$name$callbackCount',
161+
);
162+
}
163+
}
164+
}
165+
166+
runNotifiyListenersLoopWithObserverList(_kNumWarmUp, addResult: false);
167+
runNotifiyListenersLoopWithObserverList(_kNumIterationsList);
168+
169+
runNotifiyListenersLoopWithObserverList(_kNumWarmUp,
170+
failRemoval: true, addResult: false);
171+
runNotifiyListenersLoopWithObserverList(_kNumIterationsList,
172+
failRemoval: true);
173+
174+
runNotifiyListenersLoopWithHashedObserverList(_kNumWarmUp, addResult: false);
175+
runNotifiyListenersLoopWithHashedObserverList(_kNumIterationsHashed);
176+
177+
runNotifiyListenersLoopWithAnimationController(_kNumWarmUp, addResult: false);
178+
runNotifiyListenersLoopWithAnimationController(_kNumIterationsHashed);
179+
180+
printer.printToStdout();
181+
}

dev/devicelab/lib/tasks/microbenchmarks.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ TaskFunction createMicrobenchmarkTask({
5757
...await runMicrobench('lib/foundation/all_elements_bench.dart'),
5858
...await runMicrobench('lib/foundation/change_notifier_bench.dart'),
5959
...await runMicrobench('lib/foundation/clamp.dart'),
60+
...await runMicrobench('lib/foundation/observer_list_bench.dart'),
6061
...await runMicrobench('lib/foundation/platform_asset_bundle.dart'),
6162
...await runMicrobench('lib/foundation/standard_message_codec_bench.dart'),
6263
...await runMicrobench('lib/foundation/standard_method_codec_bench.dart'),

0 commit comments

Comments
 (0)