diff --git a/dev/devicelab/lib/tasks/gallery.dart b/dev/devicelab/lib/tasks/gallery.dart index 08d0851972..820eaf5504 100644 --- a/dev/devicelab/lib/tasks/gallery.dart +++ b/dev/devicelab/lib/tasks/gallery.dart @@ -139,6 +139,22 @@ class GalleryTransitionTest { 'worst_frame_rasterizer_time_millis', '90th_percentile_frame_rasterizer_time_millis', '99th_percentile_frame_rasterizer_time_millis', + 'average_layer_cache_count', + '90th_percentile_layer_cache_count', + '99th_percentile_layer_cache_count', + 'worst_layer_cache_count', + 'average_layer_cache_memory', + '90th_percentile_layer_cache_memory', + '99th_percentile_layer_cache_memory', + 'worst_layer_cache_memory', + 'average_picture_cache_count', + '90th_percentile_picture_cache_count', + '99th_percentile_picture_cache_count', + 'worst_picture_cache_count', + 'average_picture_cache_memory', + '90th_percentile_picture_cache_memory', + '99th_percentile_picture_cache_memory', + 'worst_picture_cache_memory', ], ); } diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart index 7fd1840c2f..d387e76972 100644 --- a/dev/devicelab/lib/tasks/perf_tests.dart +++ b/dev/devicelab/lib/tasks/perf_tests.dart @@ -878,6 +878,22 @@ const List _kCommonScoreKeys = [ 'worst_frame_rasterizer_time_millis', '90th_percentile_frame_rasterizer_time_millis', '99th_percentile_frame_rasterizer_time_millis', + 'average_layer_cache_count', + '90th_percentile_layer_cache_count', + '99th_percentile_layer_cache_count', + 'worst_layer_cache_count', + 'average_layer_cache_memory', + '90th_percentile_layer_cache_memory', + '99th_percentile_layer_cache_memory', + 'worst_layer_cache_memory', + 'average_picture_cache_count', + '90th_percentile_picture_cache_count', + '99th_percentile_picture_cache_count', + 'worst_picture_cache_count', + 'average_picture_cache_memory', + '90th_percentile_picture_cache_memory', + '99th_percentile_picture_cache_memory', + 'worst_picture_cache_memory', 'new_gen_gc_count', 'old_gen_gc_count', ]; diff --git a/packages/flutter_driver/lib/src/driver/raster_cache_summarizer.dart b/packages/flutter_driver/lib/src/driver/raster_cache_summarizer.dart new file mode 100644 index 0000000000..2b750116a8 --- /dev/null +++ b/packages/flutter_driver/lib/src/driver/raster_cache_summarizer.dart @@ -0,0 +1,120 @@ +// 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 'percentile_utils.dart'; +import 'timeline.dart'; + +/// Key for RasterCache timeline events. +const String kRasterCacheEvent = 'RasterCache'; + +const String _kLayerCount = 'LayerCount'; +const String _kLayerMemory = 'LayerMBytes'; +const String _kPictureCount = 'PictureCount'; +const String _kPictureMemory = 'PictureMBytes'; + +/// Summarizes [TimelineEvents]s corresponding to [kRasterCacheEvent] events. +/// +/// A sample event (some fields have been omitted for brevity): +/// ``` +/// { +/// "name": "RasterCache", +/// "ts": 75598996256, +/// "ph": "C", +/// "args": { +/// "LayerCount": "1", +/// "LayerMBytes": "0.336491", +/// "PictureCount": "0", +/// "PictureMBytes": "0.000000", +/// } +/// }, +/// ``` +class RasterCacheSummarizer { + /// Creates a RasterCacheSummarizer given the timeline events. + RasterCacheSummarizer(this.rasterCacheEvents) { + for (final TimelineEvent event in rasterCacheEvents) { + assert(event.name == kRasterCacheEvent); + } + } + + /// The raster cache events. + final List rasterCacheEvents; + + late final List _layerCounts = _extractValues(_kLayerCount); + late final List _layerMemories = _extractValues(_kLayerMemory); + late final List _pictureCounts = _extractValues(_kPictureCount); + late final List _pictureMemories = _extractValues(_kPictureMemory); + + /// Computes the average of the `LayerCount` values over the cache events. + double computeAverageLayerCount() => _computeAverage(_layerCounts); + + /// Computes the average of the `LayerMemory` values over the cache events. + double computeAverageLayerMemory() => _computeAverage(_layerMemories); + + /// Computes the average of the `PictureCount` values over the cache events. + double computeAveragePictureCount() => _computeAverage(_pictureCounts); + + /// Computes the average of the `PictureMemory` values over the cache events. + double computeAveragePictureMemory() => _computeAverage(_pictureMemories); + + /// The [percentile]-th percentile `LayerCount` over the cache events. + double computePercentileLayerCount(double percentile) => _computePercentile(_layerCounts, percentile); + + /// The [percentile]-th percentile `LayerMemory` over the cache events. + double computePercentileLayerMemory(double percentile) => _computePercentile(_layerMemories, percentile); + + /// The [percentile]-th percentile `PictureCount` over the cache events. + double computePercentilePictureCount(double percentile) => _computePercentile(_pictureCounts, percentile); + + /// The [percentile]-th percentile `PictureMemory` over the cache events. + double computePercentilePictureMemory(double percentile) => _computePercentile(_pictureMemories, percentile); + + /// Computes the worst of the `LayerCount` values over the cache events. + double computeWorstLayerCount() => _computeWorst(_layerCounts); + + /// Computes the worst of the `LayerMemory` values over the cache events. + double computeWorstLayerMemory() => _computeWorst(_layerMemories); + + /// Computes the worst of the `PictureCount` values over the cache events. + double computeWorstPictureCount() => _computeWorst(_pictureCounts); + + /// Computes the worst of the `PictureMemory` values over the cache events. + double computeWorstPictureMemory() => _computeWorst(_pictureMemories); + + static double _computeAverage(List values) { + if (values.isEmpty) { + return 0; + } + + final double total = values.reduce((double a, double b) => a + b); + return total / values.length; + } + + static double _computePercentile(List values, double percentile) { + if (values.isEmpty) { + return 0; + } + + return findPercentile(values, percentile); + } + + static double _computeWorst(List values) { + if (values.isEmpty) { + return 0; + } + + values.sort(); + return values.last; + } + + List _extractValues(String name) => + rasterCacheEvents.map((TimelineEvent e) => _getValue(e, name)).toList(); + + double _getValue(TimelineEvent e, String name) { + assert(e.name == kRasterCacheEvent); + assert(e.arguments!.containsKey(name)); + final dynamic valueString = e.arguments![name]; + assert(valueString is String); + return double.parse(valueString as String); + } +} diff --git a/packages/flutter_driver/lib/src/driver/timeline_summary.dart b/packages/flutter_driver/lib/src/driver/timeline_summary.dart index 979d17a448..18da652f53 100644 --- a/packages/flutter_driver/lib/src/driver/timeline_summary.dart +++ b/packages/flutter_driver/lib/src/driver/timeline_summary.dart @@ -11,6 +11,7 @@ import 'package:path/path.dart' as path; import 'common.dart'; import 'percentile_utils.dart'; import 'profiling_summarizer.dart'; +import 'raster_cache_summarizer.dart'; import 'scene_display_lag_summarizer.dart'; import 'timeline.dart'; import 'vsync_frame_lag_summarizer.dart'; @@ -172,10 +173,51 @@ class TimelineSummary { /// The 90/99-th percentile delay between platform vsync signal and engine /// frame process start time. /// See [VsyncFrameLagSummarizer.computePercentileVsyncFrameLag]. + /// * "average_layer_cache_count": The average of the values seen for the + /// count of the engine layer cache entries. + /// See [RasterCacheSummarizer.computeAverageLayerCount]. + /// * "90th_percentile_layer_cache_count" and + /// "99th_percentile_layer_cache_count": The 90/99-th percentile values seen + /// for the count of the engine layer cache entries. + /// See [RasterCacheSummarizer.computePercentileLayerCount]. + /// * "worst_layer_cache_count": The worst (highest) value seen for the + /// count of the engine layer cache entries. + /// See [RasterCacheSummarizer.computeWorstLayerCount]. + /// * "average_layer_cache_memory": The average of the values seen for the + /// memory used for the engine layer cache entries, in megabytes. + /// See [RasterCacheSummarizer.computeAverageLayerMemory]. + /// * "90th_percentile_layer_cache_memory" and + /// "99th_percentile_layer_cache_memory": The 90/99-th percentile values seen + /// for the memory used for the engine layer cache entries. + /// See [RasterCacheSummarizer.computePercentileLayerMemory]. + /// * "worst_layer_cache_memory": The worst (highest) value seen for the + /// memory used for the engine layer cache entries. + /// See [RasterCacheSummarizer.computeWorstLayerMemory]. + /// * "average_picture_cache_count": The average of the values seen for the + /// count of the engine picture cache entries. + /// See [RasterCacheSummarizer.computeAveragePictureCount]. + /// * "90th_percentile_picture_cache_count" and + /// "99th_percentile_picture_cache_count": The 90/99-th percentile values seen + /// for the count of the engine picture cache entries. + /// See [RasterCacheSummarizer.computePercentilePictureCount]. + /// * "worst_picture_cache_count": The worst (highest) value seen for the + /// count of the engine picture cache entries. + /// See [RasterCacheSummarizer.computeWorstPictureCount]. + /// * "average_picture_cache_memory": The average of the values seen for the + /// memory used for the engine picture cache entries, in megabytes. + /// See [RasterCacheSummarizer.computeAveragePictureMemory]. + /// * "90th_percentile_picture_cache_memory" and + /// "99th_percentile_picture_cache_memory": The 90/99-th percentile values seen + /// for the memory used for the engine picture cache entries. + /// See [RasterCacheSummarizer.computePercentilePictureMemory]. + /// * "worst_picture_cache_memory": The worst (highest) value seen for the + /// memory used for the engine picture cache entries. + /// See [RasterCacheSummarizer.computeWorstPictureMemory]. Map get summaryJson { final SceneDisplayLagSummarizer sceneDisplayLagSummarizer = _sceneDisplayLagSummarizer(); final VsyncFrameLagSummarizer vsyncFrameLagSummarizer = _vsyncFrameLagSummarizer(); final Map profilingSummary = _profilingSummarizer().summarize(); + final RasterCacheSummarizer rasterCacheSummarizer = _rasterCacheSummarizer(); final Map timelineSummary = { 'average_frame_build_time_millis': computeAverageFrameBuildTimeMillis(), @@ -210,6 +252,22 @@ class TimelineSummary { 'average_vsync_frame_lag': vsyncFrameLagSummarizer.computeAverageVsyncFrameLag(), '90th_percentile_vsync_frame_lag': vsyncFrameLagSummarizer.computePercentileVsyncFrameLag(90.0), '99th_percentile_vsync_frame_lag': vsyncFrameLagSummarizer.computePercentileVsyncFrameLag(99.0), + 'average_layer_cache_count': rasterCacheSummarizer.computeAverageLayerCount(), + '90th_percentile_layer_cache_count': rasterCacheSummarizer.computePercentileLayerCount(90.0), + '99th_percentile_layer_cache_count': rasterCacheSummarizer.computePercentileLayerCount(99.0), + 'worst_layer_cache_count': rasterCacheSummarizer.computeWorstLayerCount(), + 'average_layer_cache_memory': rasterCacheSummarizer.computeAverageLayerMemory(), + '90th_percentile_layer_cache_memory': rasterCacheSummarizer.computePercentileLayerMemory(90.0), + '99th_percentile_layer_cache_memory': rasterCacheSummarizer.computePercentileLayerMemory(99.0), + 'worst_layer_cache_memory': rasterCacheSummarizer.computeWorstLayerMemory(), + 'average_picture_cache_count': rasterCacheSummarizer.computeAveragePictureCount(), + '90th_percentile_picture_cache_count': rasterCacheSummarizer.computePercentilePictureCount(90.0), + '99th_percentile_picture_cache_count': rasterCacheSummarizer.computePercentilePictureCount(99.0), + 'worst_picture_cache_count': rasterCacheSummarizer.computeWorstPictureCount(), + 'average_picture_cache_memory': rasterCacheSummarizer.computeAveragePictureMemory(), + '90th_percentile_picture_cache_memory': rasterCacheSummarizer.computePercentilePictureMemory(90.0), + '99th_percentile_picture_cache_memory': rasterCacheSummarizer.computePercentilePictureMemory(99.0), + 'worst_picture_cache_memory': rasterCacheSummarizer.computeWorstPictureMemory(), }; timelineSummary.addAll(profilingSummary); @@ -372,4 +430,6 @@ class TimelineSummary { List _extractFrameDurations() => _extractBeginEndEvents(kBuildFrameEventName); VsyncFrameLagSummarizer _vsyncFrameLagSummarizer() => VsyncFrameLagSummarizer(_extractEventsWithNames(kVsyncTimelineEventNames)); + + RasterCacheSummarizer _rasterCacheSummarizer() => RasterCacheSummarizer(_extractNamedEvents(kRasterCacheEvent)); } diff --git a/packages/flutter_driver/test/src/real_tests/timeline_summary_test.dart b/packages/flutter_driver/test/src/real_tests/timeline_summary_test.dart index a62b20413e..ad645c222f 100644 --- a/packages/flutter_driver/test/src/real_tests/timeline_summary_test.dart +++ b/packages/flutter_driver/test/src/real_tests/timeline_summary_test.dart @@ -451,6 +451,22 @@ void main() { 'average_vsync_frame_lag': 0.0, '90th_percentile_vsync_frame_lag': 0.0, '99th_percentile_vsync_frame_lag': 0.0, + 'average_layer_cache_count': 0.0, + '90th_percentile_layer_cache_count': 0.0, + '99th_percentile_layer_cache_count': 0.0, + 'worst_layer_cache_count': 0.0, + 'average_layer_cache_memory': 0.0, + '90th_percentile_layer_cache_memory': 0.0, + '99th_percentile_layer_cache_memory': 0.0, + 'worst_layer_cache_memory': 0.0, + 'average_picture_cache_count': 0.0, + '90th_percentile_picture_cache_count': 0.0, + '99th_percentile_picture_cache_count': 0.0, + 'worst_picture_cache_count': 0.0, + 'average_picture_cache_memory': 0.0, + '90th_percentile_picture_cache_memory': 0.0, + '99th_percentile_picture_cache_memory': 0.0, + 'worst_picture_cache_memory': 0.0, }, ); }); @@ -550,6 +566,22 @@ void main() { 'average_memory_usage': 67.5, '90th_percentile_memory_usage': 75.0, '99th_percentile_memory_usage': 75.0, + 'average_layer_cache_count': 0.0, + '90th_percentile_layer_cache_count': 0.0, + '99th_percentile_layer_cache_count': 0.0, + 'worst_layer_cache_count': 0.0, + 'average_layer_cache_memory': 0.0, + '90th_percentile_layer_cache_memory': 0.0, + '99th_percentile_layer_cache_memory': 0.0, + 'worst_layer_cache_memory': 0.0, + 'average_picture_cache_count': 0.0, + '90th_percentile_picture_cache_count': 0.0, + '99th_percentile_picture_cache_count': 0.0, + 'worst_picture_cache_count': 0.0, + 'average_picture_cache_memory': 0.0, + '90th_percentile_picture_cache_memory': 0.0, + '99th_percentile_picture_cache_memory': 0.0, + 'worst_picture_cache_memory': 0.0, }); }); }); diff --git a/packages/flutter_test/lib/src/frame_timing_summarizer.dart b/packages/flutter_test/lib/src/frame_timing_summarizer.dart index 6573e5f47f..55bb67b97f 100644 --- a/packages/flutter_test/lib/src/frame_timing_summarizer.dart +++ b/packages/flutter_test/lib/src/frame_timing_summarizer.dart @@ -37,9 +37,26 @@ class FrameTimingSummarizer { final List vsyncOverhead = List.unmodifiable( data.map((FrameTiming datum) => datum.vsyncOverhead), ); + final List layerCacheCounts = List.unmodifiable( + data.map((FrameTiming datum) => datum.layerCacheCount), + ); + final List layerCacheCountsSorted = List.from(layerCacheCounts)..sort(); + final List layerCacheBytes = List.unmodifiable( + data.map((FrameTiming datum) => datum.layerCacheBytes), + ); + final List layerCacheBytesSorted = List.from(layerCacheBytes)..sort(); + final List pictureCacheCounts = List.unmodifiable( + data.map((FrameTiming datum) => datum.pictureCacheCount), + ); + final List pictureCacheCountsSorted = List.from(pictureCacheCounts)..sort(); + final List pictureCacheBytes = List.unmodifiable( + data.map((FrameTiming datum) => datum.pictureCacheBytes), + ); + final List pictureCacheBytesSorted = List.from(pictureCacheBytes)..sort(); final List vsyncOverheadSorted = List.from(vsyncOverhead)..sort(); Duration add(Duration a, Duration b) => a + b; + int addInts(int a, int b) => a + b; return FrameTimingSummarizer._( frameBuildTime: frameBuildTime, frameRasterizerTime: frameRasterizerTime, @@ -56,6 +73,22 @@ class FrameTimingSummarizer { p90FrameRasterizerTime: _findPercentile(frameRasterizerTimeSorted, 0.90), p99FrameRasterizerTime: _findPercentile(frameRasterizerTimeSorted, 0.99), worstFrameRasterizerTime: frameRasterizerTimeSorted.last, + averageLayerCacheCount: layerCacheCounts.reduce(addInts) / data.length, + p90LayerCacheCount: _findPercentile(layerCacheCountsSorted, 0.90), + p99LayerCacheCount: _findPercentile(layerCacheCountsSorted, 0.99), + worstLayerCacheCount: layerCacheCountsSorted.last, + averageLayerCacheBytes: layerCacheBytes.reduce(addInts) / data.length, + p90LayerCacheBytes: _findPercentile(layerCacheBytesSorted, 0.90), + p99LayerCacheBytes: _findPercentile(layerCacheBytesSorted, 0.99), + worstLayerCacheBytes: layerCacheBytesSorted.last, + averagePictureCacheCount: pictureCacheCounts.reduce(addInts) / data.length, + p90PictureCacheCount: _findPercentile(pictureCacheCountsSorted, 0.90), + p99PictureCacheCount: _findPercentile(pictureCacheCountsSorted, 0.99), + worstPictureCacheCount: pictureCacheCountsSorted.last, + averagePictureCacheBytes: pictureCacheBytes.reduce(addInts) / data.length, + p90PictureCacheBytes: _findPercentile(pictureCacheBytesSorted, 0.90), + p99PictureCacheBytes: _findPercentile(pictureCacheBytesSorted, 0.99), + worstPictureCacheBytes: pictureCacheBytesSorted.last, missedFrameRasterizerBudget: _countExceed(frameRasterizerTimeSorted, kBuildBudget), averageVsyncOverhead: vsyncOverhead.reduce(add) ~/ data.length, @@ -79,6 +112,22 @@ class FrameTimingSummarizer { required this.p90FrameRasterizerTime, required this.p99FrameRasterizerTime, required this.worstFrameRasterizerTime, + required this.averageLayerCacheCount, + required this.p90LayerCacheCount, + required this.p99LayerCacheCount, + required this.worstLayerCacheCount, + required this.averageLayerCacheBytes, + required this.p90LayerCacheBytes, + required this.p99LayerCacheBytes, + required this.worstLayerCacheBytes, + required this.averagePictureCacheCount, + required this.p90PictureCacheCount, + required this.p99PictureCacheCount, + required this.worstPictureCacheCount, + required this.averagePictureCacheBytes, + required this.p90PictureCacheBytes, + required this.p99PictureCacheBytes, + required this.worstPictureCacheBytes, required this.missedFrameRasterizerBudget, required this.vsyncOverhead, required this.averageVsyncOverhead, @@ -126,6 +175,54 @@ class FrameTimingSummarizer { /// The largest value of [frameRasterizerTime] in milliseconds. final Duration worstFrameRasterizerTime; + /// The average number of layers cached across all frames. + final double averageLayerCacheCount; + + /// The 90-th percentile number of layers cached across all frames. + final int p90LayerCacheCount; + + /// The 90-th percentile number of layers cached across all frames. + final int p99LayerCacheCount; + + /// The most number of layers cached across all frames. + final int worstLayerCacheCount; + + /// The average number of bytes consumed by cached layers across all frames. + final double averageLayerCacheBytes; + + /// The 90-th percentile number of bytes consumed by cached layers across all frames. + final int p90LayerCacheBytes; + + /// The 90-th percentile number of bytes consumed by cached layers across all frames. + final int p99LayerCacheBytes; + + /// The highest number of bytes consumed by cached layers across all frames. + final int worstLayerCacheBytes; + + /// The average number of pictures cached across all frames. + final double averagePictureCacheCount; + + /// The 90-th percentile number of pictures cached across all frames. + final int p90PictureCacheCount; + + /// The 90-th percentile number of pictures cached across all frames. + final int p99PictureCacheCount; + + /// The most number of pictures cached across all frames. + final int worstPictureCacheCount; + + /// The average number of bytes consumed by cached pictures across all frames. + final double averagePictureCacheBytes; + + /// The 90-th percentile number of bytes consumed by cached pictures across all frames. + final int p90PictureCacheBytes; + + /// The 90-th percentile number of bytes consumed by cached pictures across all frames. + final int p99PictureCacheBytes; + + /// The highest number of bytes consumed by cached pictures across all frames. + final int worstPictureCacheBytes; + /// Number of items in [frameRasterizerTime] that's greater than [kBuildBudget] final int missedFrameRasterizerBudget; @@ -168,6 +265,22 @@ class FrameTimingSummarizer { p99FrameRasterizerTime.inMicroseconds / 1E3, 'worst_frame_rasterizer_time_millis': worstFrameRasterizerTime.inMicroseconds / 1E3, + 'average_layer_cache_count': averageLayerCacheCount, + '90th_percentile_layer_cache_count': p90LayerCacheCount, + '99th_percentile_layer_cache_count': p99LayerCacheCount, + 'worst_layer_cache_count': worstLayerCacheCount, + 'average_layer_cache_memory': averageLayerCacheBytes / 1024.0 / 1024.0, + '90th_percentile_layer_cache_memory': p90LayerCacheBytes / 1024.0 / 1024.0, + '99th_percentile_layer_cache_memory': p99LayerCacheBytes / 1024.0 / 1024.0, + 'worst_layer_cache_memory': worstLayerCacheBytes / 1024.0 / 1024.0, + 'average_picture_cache_count': averagePictureCacheCount, + '90th_percentile_picture_cache_count': p90PictureCacheCount, + '99th_percentile_picture_cache_count': p99PictureCacheCount, + 'worst_picture_cache_count': worstPictureCacheCount, + 'average_picture_cache_memory': averagePictureCacheBytes / 1024.0 / 1024.0, + '90th_percentile_picture_cache_memory': p90PictureCacheBytes / 1024.0 / 1024.0, + '99th_percentile_picture_cache_memory': p99PictureCacheBytes / 1024.0 / 1024.0, + 'worst_picture_cache_memory': worstPictureCacheBytes / 1024.0 / 1024.0, 'missed_frame_rasterizer_budget_count': missedFrameRasterizerBudget, 'frame_count': frameBuildTime.length, 'frame_build_times': frameBuildTime