Add benchmark for number of GCs in animated GIF (#81240)
This commit is contained in:
parent
08d7811f7f
commit
0866005f70
@ -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';
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
Navigator.pushNamed(context, kAnimationWithMicrotasksRouteName);
|
Navigator.pushNamed(context, kAnimationWithMicrotasksRouteName);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
key: const Key(kAnimatedImageRouteName),
|
||||||
|
child: const Text('Animated Image'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pushNamed(context, kAnimatedImageRouteName);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
30
dev/benchmarks/macrobenchmarks/lib/src/animated_image.dart
Normal file
30
dev/benchmarks/macrobenchmarks/lib/src/animated_image.dart
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 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:
|
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
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
// 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();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
// 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();
|
||||||
|
});
|
||||||
|
}
|
16
dev/devicelab/bin/tasks/animated_image_gc_perf.dart
Normal file
16
dev/devicelab/bin/tasks/animated_image_gc_perf.dart
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 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',
|
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 @@
|
|||||||
"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 {
|
|||||||
/// 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 {
|
|||||||
'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() {
|
|||||||
'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() {
|
|||||||
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() {
|
|||||||
'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() {
|
|||||||
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() {
|
|||||||
'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],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user