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';
const String kSimpleScrollRouteName = '/simple_scroll';
const String kStackSizeRouteName = '/stack_size';
const String kAnimationWithMicrotasksRouteName = '/animation_with_microtasks';
const String kAnimatedImageRouteName = '/animated_image';
const String kScrollableName = '/macrobenchmark_listview';
......
......@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'common.dart';
import 'src/animated_image.dart';
import 'src/animated_placeholder.dart';
import 'src/animation_with_microtasks.dart';
import 'src/backdrop_filter.dart';
......@@ -58,6 +59,7 @@ class MacrobenchmarksApp extends StatelessWidget {
kSimpleScrollRouteName: (BuildContext context) => const SimpleScroll(),
kStackSizeRouteName: (BuildContext context) => const StackSizePage(),
kAnimationWithMicrotasksRouteName: (BuildContext context) => const AnimationWithMicrotasks(),
kAnimatedImageRouteName: (BuildContext context) => const AnimatedImagePage(),
},
);
}
......@@ -201,6 +203,13 @@ class HomePage extends StatelessWidget {
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:
flutter:
uses-material-design: true
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/cherry_pie.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 {
exampleCodeTag: 'animated_image',
demoWidget: Semantics(
label: 'Example of animated GIF',
child:Image.asset(
child: Image.asset(
'animated_images/animated_flutter_lgtm.gif',
package: 'flutter_gallery_assets',
),
......
......@@ -324,6 +324,12 @@
"task_name": "linux_large_image_changer_perf_android",
"flaky": false
},
{
"name": "Linux animated_image_gc_perf",
"repo": "flutter",
"task_name": "animated_image_gc_perf",
"flaky": true
},
{
"name": "Linux linux_chrome_dev_mode",
"repo": "flutter",
......
......@@ -95,6 +95,20 @@ class TimelineSummary {
/// The total number of rasterizer cycles recorded in the timeline.
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.
///
/// Data ends with "_time_millis" means time in milliseconds and numbers in
......@@ -176,6 +190,8 @@ class TimelineSummary {
'missed_frame_rasterizer_budget_count': computeMissedFrameRasterizerBudgetCount(),
'frame_count': countFrames(),
'frame_rasterizer_count': countRasterizations(),
'new_gen_gc_count': newGenerationGarbageCollections(),
'old_gen_gc_count': oldGenerationGarbageCollections(),
'frame_build_times': _extractFrameDurations()
.map<int>((Duration duration) => duration.inMicroseconds)
.toList(),
......
......@@ -95,6 +95,38 @@ void main() {
'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) {
final List<Map<String, dynamic>> result = <Map<String, dynamic>>[];
int t = 0;
......@@ -388,6 +420,8 @@ void main() {
begin(1000), end(19000),
begin(19000), end(29000),
begin(29000), end(49000),
...newGenGC(4),
...oldGenGC(5),
frameBegin(1000), frameEnd(18000),
frameBegin(19000), frameEnd(28000),
frameBegin(29000), frameEnd(48000),
......@@ -405,6 +439,8 @@ void main() {
'missed_frame_rasterizer_budget_count': 2,
'frame_count': 3,
'frame_rasterizer_count': 3,
'new_gen_gc_count': 4,
'old_gen_gc_count': 5,
'frame_build_times': <int>[17000, 9000, 19000],
'frame_rasterizer_times': <int>[18000, 10000, 20000],
'frame_begin_times': <int>[0, 18000, 28000],
......@@ -475,6 +511,8 @@ void main() {
lagBegin(1000, 4), lagEnd(2000, 4),
lagBegin(1200, 12), lagEnd(2400, 12),
lagBegin(4200, 8), lagEnd(9400, 8),
...newGenGC(4),
...oldGenGC(5),
cpuUsage(5000, 20), cpuUsage(5010, 60),
memoryUsage(6000, 20, 40), memoryUsage(6100, 30, 45),
platformVsync(7000), vsyncCallback(7500),
......@@ -494,6 +532,8 @@ void main() {
'missed_frame_rasterizer_budget_count': 2,
'frame_count': 3,
'frame_rasterizer_count': 3,
'new_gen_gc_count': 4,
'old_gen_gc_count': 5,
'frame_build_times': <int>[17000, 9000, 19000],
'frame_rasterizer_times': <int>[18000, 10000, 20000],
'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