Unverified Commit 0866005f authored by Dan Field's avatar Dan Field Committed by GitHub

Add benchmark for number of GCs in animated GIF (#81240)

parent 08d7811f
...@@ -21,6 +21,7 @@ const String kHeavyGridViewRouteName = '/heavy_gridview'; ...@@ -21,6 +21,7 @@ const String kHeavyGridViewRouteName = '/heavy_gridview';
const String kSimpleScrollRouteName = '/simple_scroll'; const String kSimpleScrollRouteName = '/simple_scroll';
const String kStackSizeRouteName = '/stack_size'; const String kStackSizeRouteName = '/stack_size';
const String kAnimationWithMicrotasksRouteName = '/animation_with_microtasks'; const String kAnimationWithMicrotasksRouteName = '/animation_with_microtasks';
const String kAnimatedImageRouteName = '/animated_image';
const String kScrollableName = '/macrobenchmark_listview'; const String kScrollableName = '/macrobenchmark_listview';
......
...@@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; ...@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'common.dart'; import 'common.dart';
import 'src/animated_image.dart';
import 'src/animated_placeholder.dart'; import 'src/animated_placeholder.dart';
import 'src/animation_with_microtasks.dart'; import 'src/animation_with_microtasks.dart';
import 'src/backdrop_filter.dart'; import 'src/backdrop_filter.dart';
...@@ -58,6 +59,7 @@ class MacrobenchmarksApp extends StatelessWidget { ...@@ -58,6 +59,7 @@ class MacrobenchmarksApp extends StatelessWidget {
kSimpleScrollRouteName: (BuildContext context) => const SimpleScroll(), kSimpleScrollRouteName: (BuildContext context) => const SimpleScroll(),
kStackSizeRouteName: (BuildContext context) => const StackSizePage(), kStackSizeRouteName: (BuildContext context) => const StackSizePage(),
kAnimationWithMicrotasksRouteName: (BuildContext context) => const AnimationWithMicrotasks(), kAnimationWithMicrotasksRouteName: (BuildContext context) => const AnimationWithMicrotasks(),
kAnimatedImageRouteName: (BuildContext context) => const AnimatedImagePage(),
}, },
); );
} }
...@@ -201,6 +203,13 @@ class HomePage extends StatelessWidget { ...@@ -201,6 +203,13 @@ class HomePage extends StatelessWidget {
Navigator.pushNamed(context, kAnimationWithMicrotasksRouteName); Navigator.pushNamed(context, kAnimationWithMicrotasksRouteName);
}, },
), ),
ElevatedButton(
key: const Key(kAnimatedImageRouteName),
child: const Text('Animated Image'),
onPressed: () {
Navigator.pushNamed(context, kAnimatedImageRouteName);
},
),
], ],
), ),
); );
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
class AnimatedImagePage extends StatelessWidget {
const AnimatedImagePage({Key key, this.onFrame}) : super(key: key);
final ValueChanged<int> onFrame;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Animated Image'),
),
body: Image.asset(
'animated_images/animated_flutter_lgtm.gif',
package: 'flutter_gallery_assets',
frameBuilder: (BuildContext context, Widget child, int/*?*/ frame, bool syncCall) {
if (onFrame != null && frame != null) {
onFrame(frame);
}
return child;
},
),
);
}
}
...@@ -81,6 +81,7 @@ dev_dependencies: ...@@ -81,6 +81,7 @@ dev_dependencies:
flutter: flutter:
uses-material-design: true uses-material-design: true
assets: assets:
- packages/flutter_gallery_assets/animated_images/animated_flutter_lgtm.gif
- packages/flutter_gallery_assets/food/butternut_squash_soup.png - packages/flutter_gallery_assets/food/butternut_squash_soup.png
- packages/flutter_gallery_assets/food/cherry_pie.png - packages/flutter_gallery_assets/food/cherry_pie.png
- assets/999x1000.png - assets/999x1000.png
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:macrobenchmarks/src/animated_image.dart';
/// This test is slightly different than most of the other tests in this
/// application, in that it directly instantiates the page we care about and
/// passes a callback. This way, we can make sure to consistently wait for a
/// set number of image frames to render.
Future<void> main() async {
final Completer<void> waiter = Completer<void>();
enableFlutterDriverExtension(handler: (String request) async {
if (request != 'waitForAnimation') {
throw UnsupportedError('Unrecognized request $request');
}
await waiter.future;
return 'done';
});
runApp(MaterialApp(
home: AnimatedImagePage(
onFrame: (int frameNumber) {
if (frameNumber == 250) {
waiter.complete();
}
},
),
));
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
Future<void> main() async {
const String fileName = 'large_image_changer';
test('Animate for 250 frames', () async {
final FlutterDriver driver = await FlutterDriver.connect();
await driver.forceGC();
final Timeline timeline = await driver.traceAction(() async {
await driver.requestData('waitForAnimation');
});
final TimelineSummary summary = TimelineSummary.summarize(timeline);
await summary.writeTimelineToFile(fileName, pretty: true);
await driver.close();
});
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:flutter_devicelab/tasks/perf_tests.dart';
Future<void> main() async {
deviceOperatingSystem = DeviceOperatingSystem.android;
await task(DevToolsMemoryTest(
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
'test_driver/animated_image.dart',
).run);
}
...@@ -34,7 +34,7 @@ class ImagesDemo extends StatelessWidget { ...@@ -34,7 +34,7 @@ class ImagesDemo extends StatelessWidget {
exampleCodeTag: 'animated_image', exampleCodeTag: 'animated_image',
demoWidget: Semantics( demoWidget: Semantics(
label: 'Example of animated GIF', label: 'Example of animated GIF',
child:Image.asset( child: Image.asset(
'animated_images/animated_flutter_lgtm.gif', 'animated_images/animated_flutter_lgtm.gif',
package: 'flutter_gallery_assets', package: 'flutter_gallery_assets',
), ),
......
...@@ -324,6 +324,12 @@ ...@@ -324,6 +324,12 @@
"task_name": "linux_large_image_changer_perf_android", "task_name": "linux_large_image_changer_perf_android",
"flaky": false "flaky": false
}, },
{
"name": "Linux animated_image_gc_perf",
"repo": "flutter",
"task_name": "animated_image_gc_perf",
"flaky": true
},
{ {
"name": "Linux linux_chrome_dev_mode", "name": "Linux linux_chrome_dev_mode",
"repo": "flutter", "repo": "flutter",
......
...@@ -95,6 +95,20 @@ class TimelineSummary { ...@@ -95,6 +95,20 @@ class TimelineSummary {
/// The total number of rasterizer cycles recorded in the timeline. /// The total number of rasterizer cycles recorded in the timeline.
int countRasterizations() => _extractGpuRasterizerDrawDurations().length; int countRasterizations() => _extractGpuRasterizerDrawDurations().length;
/// The total number of old generation garbage collections recorded in the timeline.
int oldGenerationGarbageCollections() {
return _timeline.events!.where((TimelineEvent event) {
return event.category == 'GC' && event.name == 'CollectOldGeneration';
}).length;
}
/// The total number of new generation garbage collections recorded in the timeline.
int newGenerationGarbageCollections() {
return _timeline.events!.where((TimelineEvent event) {
return event.category == 'GC' && event.name == 'CollectNewGeneration';
}).length;
}
/// Encodes this summary as JSON. /// Encodes this summary as JSON.
/// ///
/// Data ends with "_time_millis" means time in milliseconds and numbers in /// Data ends with "_time_millis" means time in milliseconds and numbers in
...@@ -176,6 +190,8 @@ class TimelineSummary { ...@@ -176,6 +190,8 @@ class TimelineSummary {
'missed_frame_rasterizer_budget_count': computeMissedFrameRasterizerBudgetCount(), 'missed_frame_rasterizer_budget_count': computeMissedFrameRasterizerBudgetCount(),
'frame_count': countFrames(), 'frame_count': countFrames(),
'frame_rasterizer_count': countRasterizations(), 'frame_rasterizer_count': countRasterizations(),
'new_gen_gc_count': newGenerationGarbageCollections(),
'old_gen_gc_count': oldGenerationGarbageCollections(),
'frame_build_times': _extractFrameDurations() 'frame_build_times': _extractFrameDurations()
.map<int>((Duration duration) => duration.inMicroseconds) .map<int>((Duration duration) => duration.inMicroseconds)
.toList(), .toList(),
......
...@@ -95,6 +95,38 @@ void main() { ...@@ -95,6 +95,38 @@ void main() {
'ts': timeStamp, 'ts': timeStamp,
}; };
List<Map<String, dynamic>> newGenGC(int count) => List<Map<String, dynamic>>.filled(
count,
<String, dynamic>{
'name': 'CollectNewGeneration',
'cat': 'GC',
'tid': 19695,
'pid': 19650,
'ts': 358849612473,
'tts': 476761,
'ph': 'B',
'args': <String, dynamic>{
'isolateGroupId': 'isolateGroups/10824099774666259225',
},
},
);
List<Map<String, dynamic>> oldGenGC(int count) => List<Map<String, dynamic>>.filled(
count,
<String, dynamic>{
'name': 'CollectOldGeneration',
'cat': 'GC',
'tid': 19695,
'pid': 19650,
'ts': 358849612473,
'tts': 476761,
'ph': 'B',
'args': <String, dynamic>{
'isolateGroupId': 'isolateGroups/10824099774666259225',
},
},
);
List<Map<String, dynamic>> rasterizeTimeSequenceInMillis(List<int> sequence) { List<Map<String, dynamic>> rasterizeTimeSequenceInMillis(List<int> sequence) {
final List<Map<String, dynamic>> result = <Map<String, dynamic>>[]; final List<Map<String, dynamic>> result = <Map<String, dynamic>>[];
int t = 0; int t = 0;
...@@ -388,6 +420,8 @@ void main() { ...@@ -388,6 +420,8 @@ void main() {
begin(1000), end(19000), begin(1000), end(19000),
begin(19000), end(29000), begin(19000), end(29000),
begin(29000), end(49000), begin(29000), end(49000),
...newGenGC(4),
...oldGenGC(5),
frameBegin(1000), frameEnd(18000), frameBegin(1000), frameEnd(18000),
frameBegin(19000), frameEnd(28000), frameBegin(19000), frameEnd(28000),
frameBegin(29000), frameEnd(48000), frameBegin(29000), frameEnd(48000),
...@@ -405,6 +439,8 @@ void main() { ...@@ -405,6 +439,8 @@ void main() {
'missed_frame_rasterizer_budget_count': 2, 'missed_frame_rasterizer_budget_count': 2,
'frame_count': 3, 'frame_count': 3,
'frame_rasterizer_count': 3, 'frame_rasterizer_count': 3,
'new_gen_gc_count': 4,
'old_gen_gc_count': 5,
'frame_build_times': <int>[17000, 9000, 19000], 'frame_build_times': <int>[17000, 9000, 19000],
'frame_rasterizer_times': <int>[18000, 10000, 20000], 'frame_rasterizer_times': <int>[18000, 10000, 20000],
'frame_begin_times': <int>[0, 18000, 28000], 'frame_begin_times': <int>[0, 18000, 28000],
...@@ -475,6 +511,8 @@ void main() { ...@@ -475,6 +511,8 @@ void main() {
lagBegin(1000, 4), lagEnd(2000, 4), lagBegin(1000, 4), lagEnd(2000, 4),
lagBegin(1200, 12), lagEnd(2400, 12), lagBegin(1200, 12), lagEnd(2400, 12),
lagBegin(4200, 8), lagEnd(9400, 8), lagBegin(4200, 8), lagEnd(9400, 8),
...newGenGC(4),
...oldGenGC(5),
cpuUsage(5000, 20), cpuUsage(5010, 60), cpuUsage(5000, 20), cpuUsage(5010, 60),
memoryUsage(6000, 20, 40), memoryUsage(6100, 30, 45), memoryUsage(6000, 20, 40), memoryUsage(6100, 30, 45),
platformVsync(7000), vsyncCallback(7500), platformVsync(7000), vsyncCallback(7500),
...@@ -494,6 +532,8 @@ void main() { ...@@ -494,6 +532,8 @@ void main() {
'missed_frame_rasterizer_budget_count': 2, 'missed_frame_rasterizer_budget_count': 2,
'frame_count': 3, 'frame_count': 3,
'frame_rasterizer_count': 3, 'frame_rasterizer_count': 3,
'new_gen_gc_count': 4,
'old_gen_gc_count': 5,
'frame_build_times': <int>[17000, 9000, 19000], 'frame_build_times': <int>[17000, 9000, 19000],
'frame_rasterizer_times': <int>[18000, 10000, 20000], 'frame_rasterizer_times': <int>[18000, 10000, 20000],
'frame_begin_times': <int>[0, 18000, 28000], 'frame_begin_times': <int>[0, 18000, 28000],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment