From a2005eaf9abae21a14448d3eafb2e0e41deef230 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Thu, 5 Dec 2019 19:28:00 -0800 Subject: [PATCH] Memory test on scrolling large images quickly (#46184) --- .../macrobenchmarks/assets/999x1000.png | Bin 0 -> 3561 bytes .../macrobenchmarks/lib/common.dart | 1 + dev/benchmarks/macrobenchmarks/lib/main.dart | 19 +++++-- .../macrobenchmarks/lib/src/large_images.dart | 48 ++++++++++++++++++ dev/benchmarks/macrobenchmarks/pubspec.yaml | 1 + .../test_memory/large_images.dart | 35 +++++++++++++ .../fast_scroll_large_images__memory.dart | 41 +++++++++++++++ dev/devicelab/manifest.yaml | 6 +++ 8 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 dev/benchmarks/macrobenchmarks/assets/999x1000.png create mode 100644 dev/benchmarks/macrobenchmarks/lib/src/large_images.dart create mode 100644 dev/benchmarks/macrobenchmarks/test_memory/large_images.dart create mode 100644 dev/devicelab/bin/tasks/fast_scroll_large_images__memory.dart diff --git a/dev/benchmarks/macrobenchmarks/assets/999x1000.png b/dev/benchmarks/macrobenchmarks/assets/999x1000.png new file mode 100644 index 0000000000000000000000000000000000000000..e9b09f7df10474e71f7567006dd8555f9ffdcaab GIT binary patch literal 3561 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 literal 0 HcmV?d00001 diff --git a/dev/benchmarks/macrobenchmarks/lib/common.dart b/dev/benchmarks/macrobenchmarks/lib/common.dart index d6902eb4dc..c8d24dc196 100644 --- a/dev/benchmarks/macrobenchmarks/lib/common.dart +++ b/dev/benchmarks/macrobenchmarks/lib/common.dart @@ -7,3 +7,4 @@ const String kCubicBezierRouteName = '/cubic_bezier'; const String kBackdropFilterRouteName = '/backdrop_filter'; const String kSimpleAnimationRouteName = '/simple_animation'; const String kPictureCacheRouteName = '/picture_cache'; +const String kLargeImagesRouteName = '/large_images'; diff --git a/dev/benchmarks/macrobenchmarks/lib/main.dart b/dev/benchmarks/macrobenchmarks/lib/main.dart index eaecea02b0..5a2160c6dd 100644 --- a/dev/benchmarks/macrobenchmarks/lib/main.dart +++ b/dev/benchmarks/macrobenchmarks/lib/main.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; +import 'package:macrobenchmarks/src/large_images.dart'; import 'package:macrobenchmarks/src/picture_cache.dart'; import 'common.dart'; @@ -13,24 +14,29 @@ import 'src/simple_animation.dart'; const String kMacrobenchmarks ='Macrobenchmarks'; -void main() => runApp(MacrobenchmarksApp()); +void main() => runApp(const MacrobenchmarksApp()); class MacrobenchmarksApp extends StatelessWidget { + const MacrobenchmarksApp({this.initialRoute = '/'}); + @override Widget build(BuildContext context) { return MaterialApp( title: kMacrobenchmarks, - initialRoute: '/', + initialRoute: initialRoute, routes: { '/': (BuildContext context) => HomePage(), kCullOpacityRouteName: (BuildContext context) => CullOpacityPage(), kCubicBezierRouteName: (BuildContext context) => CubicBezierPage(), kBackdropFilterRouteName: (BuildContext context) => BackdropFilterPage(), kSimpleAnimationRouteName: (BuildContext conttext) => SimpleAnimationPage(), - kPictureCacheRouteName: (BuildContext conttext) => PictureCachePage(), + kPictureCacheRouteName: (BuildContext context) => PictureCachePage(), + kLargeImagesRouteName: (BuildContext context) => LargeImagesPage(), }, ); } + + final String initialRoute; } class HomePage extends StatelessWidget { @@ -75,6 +81,13 @@ class HomePage extends StatelessWidget { Navigator.pushNamed(context, kPictureCacheRouteName); }, ), + RaisedButton( + key: const Key(kLargeImagesRouteName), + child: const Text('Large Images'), + onPressed: () { + Navigator.pushNamed(context, kLargeImagesRouteName); + }, + ), ], ), ); diff --git a/dev/benchmarks/macrobenchmarks/lib/src/large_images.dart b/dev/benchmarks/macrobenchmarks/lib/src/large_images.dart new file mode 100644 index 0000000000..5dd70ffc11 --- /dev/null +++ b/dev/benchmarks/macrobenchmarks/lib/src/large_images.dart @@ -0,0 +1,48 @@ +// 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 'dart:typed_data'; + +import 'package:flutter/material.dart'; + +class LargeImagesPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + final ImageCache imageCache = PaintingBinding.instance.imageCache; + imageCache.maximumSize = 30; + imageCache.maximumSizeBytes = 50 << 20; + return GridView.builder( + itemCount: 1000, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3), + itemBuilder: (BuildContext context, int index) => DummyImage(index), + ).build(context); + } +} + +class DummyImage extends StatelessWidget { + DummyImage(this.index) : super(key: ValueKey(index)); + + @override + Widget build(BuildContext context) { + final Future pngData = _getPngData(context); + + return FutureBuilder( + future: pngData, + builder: (BuildContext context, AsyncSnapshot snapshot) { + // Use Image.memory instead of Image.asset to make sure that we're + // creating many copies of the image to trigger the memory issue. + return snapshot.data == null + ? Container() + : Image.memory(snapshot.data.buffer.asUint8List()); + }, + ); + } + + final int index; + + Future _getPngData(BuildContext context) async { + return DefaultAssetBundle.of(context).load('assets/999x1000.png'); + } +} diff --git a/dev/benchmarks/macrobenchmarks/pubspec.yaml b/dev/benchmarks/macrobenchmarks/pubspec.yaml index 62a69cc5ed..471d22ca0e 100644 --- a/dev/benchmarks/macrobenchmarks/pubspec.yaml +++ b/dev/benchmarks/macrobenchmarks/pubspec.yaml @@ -90,5 +90,6 @@ flutter: assets: - packages/flutter_gallery_assets/food/butternut_squash_soup.png - packages/flutter_gallery_assets/food/cherry_pie.png + - assets/999x1000.png # PUBSPEC CHECKSUM: 4cef diff --git a/dev/benchmarks/macrobenchmarks/test_memory/large_images.dart b/dev/benchmarks/macrobenchmarks/test_memory/large_images.dart new file mode 100644 index 0000000000..62c14d6b0b --- /dev/null +++ b/dev/benchmarks/macrobenchmarks/test_memory/large_images.dart @@ -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/scheduler.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:macrobenchmarks/common.dart'; +import 'package:macrobenchmarks/main.dart'; + +Future endOfAnimation() async { + do { + await SchedulerBinding.instance.endOfFrame; + } while (SchedulerBinding.instance.hasScheduledFrame); +} + +int iteration = 0; + +class LifecycleObserver extends WidgetsBindingObserver { + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + debugPrint('==== MEMORY BENCHMARK ==== $state ===='); + debugPrint('This was lifecycle event number $iteration in this instance'); + } +} + +Future main() async { + runApp(const MacrobenchmarksApp(initialRoute: kLargeImagesRouteName)); + await endOfAnimation(); + await Future.delayed(const Duration(milliseconds: 50)); + debugPrint('==== MEMORY BENCHMARK ==== READY ===='); + WidgetsBinding.instance.addObserver(LifecycleObserver()); +} diff --git a/dev/devicelab/bin/tasks/fast_scroll_large_images__memory.dart b/dev/devicelab/bin/tasks/fast_scroll_large_images__memory.dart new file mode 100644 index 0000000000..b07f163bda --- /dev/null +++ b/dev/devicelab/bin/tasks/fast_scroll_large_images__memory.dart @@ -0,0 +1,41 @@ +// 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_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'; + +const String kPackageName = 'com.example.macrobenchmarks'; +const String kActivityName = 'com.example.macrobenchmarks.MainActivity'; + +class FastScrollLargeImagesMemoryTest extends MemoryTest { + FastScrollLargeImagesMemoryTest() + : super( + '${flutterDirectory.path}/dev/benchmarks/macrobenchmarks', + 'test_memory/large_images.dart', kPackageName, + ); + + @override + AndroidDevice get device => super.device; + + @override + int get iterationCount => 5; + + @override + Future useMemory() async { + await launchApp(); + await recordStart(); + await device.shellExec('input', ['swipe', '0 1500 0 0 50']); + await Future.delayed(const Duration(milliseconds: 10000)); + await recordEnd(); + } +} + +Future main() async { + deviceOperatingSystem = DeviceOperatingSystem.android; + await task(FastScrollLargeImagesMemoryTest().run); +} diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml index cf6f8cdeca..7df2641fe4 100644 --- a/dev/devicelab/manifest.yaml +++ b/dev/devicelab/manifest.yaml @@ -670,6 +670,12 @@ tasks: stage: devicelab required_agent_capabilities: ["linux/android"] + fast_scroll_large_images__memory: + description: > + Measures memory usage for scrolling through a list of large images. + stage: devicelab + required_agent_capabilities: ["mac/android"] + analyzer_benchmark: description: > Measures the speed of Dart analyzer.