Commit 4b401a3e authored by Ian Hickson's avatar Ian Hickson

Track the worst frame in perf tests (#3642)

* Track the worst frame in perf tests

* Use backticks in "/// Returns `null` if"
parent 4b6af7a4
...@@ -1611,7 +1611,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -1611,7 +1611,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
/// the approximate bounding box of the clip rect that would be /// the approximate bounding box of the clip rect that would be
/// applied to the given child during the paint phase, if any. /// applied to the given child during the paint phase, if any.
/// ///
/// Returns null if the child would not be clipped. /// Returns `null` if the child would not be clipped.
/// ///
/// This is used in the semantics phase to avoid including children /// This is used in the semantics phase to avoid including children
/// that are not physically visible. /// that are not physically visible.
......
...@@ -54,7 +54,7 @@ class MojoShell { ...@@ -54,7 +54,7 @@ class MojoShell {
/// Attempts to connect to an application via the Mojo shell. /// Attempts to connect to an application via the Mojo shell.
/// ///
/// Returns null if [canConnectToOtherApplications] is false. /// Returns `null` if [canConnectToOtherApplications] is false.
ApplicationConnection connectToApplication(String url) { ApplicationConnection connectToApplication(String url) {
if (_shell == null) if (_shell == null)
return null; return null;
......
...@@ -104,7 +104,7 @@ class PageStorage extends StatelessWidget { ...@@ -104,7 +104,7 @@ class PageStorage extends StatelessWidget {
/// The bucket from the closest instance of this class that encloses the given context. /// The bucket from the closest instance of this class that encloses the given context.
/// ///
/// Returns null if none exists. /// Returns `null` if none exists.
static PageStorageBucket of(BuildContext context) { static PageStorageBucket of(BuildContext context) {
PageStorage widget = context.ancestorWidgetOfExactType(PageStorage); PageStorage widget = context.ancestorWidgetOfExactType(PageStorage);
return widget?.bucket; return widget?.bucket;
......
...@@ -468,7 +468,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T ...@@ -468,7 +468,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
/// Returns the modal route most closely associated with the given context. /// Returns the modal route most closely associated with the given context.
/// ///
/// Returns null if the given context is not associated with a modal route. /// Returns `null` if the given context is not associated with a modal route.
static ModalRoute<dynamic> of(BuildContext context) { static ModalRoute<dynamic> of(BuildContext context) {
_ModalScopeStatus widget = context.inheritFromWidgetOfExactType(_ModalScopeStatus); _ModalScopeStatus widget = context.inheritFromWidgetOfExactType(_ModalScopeStatus);
return widget?.route; return widget?.route;
......
...@@ -18,7 +18,7 @@ abstract class ScrollBehavior<T, U> { ...@@ -18,7 +18,7 @@ abstract class ScrollBehavior<T, U> {
/// ///
/// This function is called when a drag gesture ends. /// This function is called when a drag gesture ends.
/// ///
/// Returns null if the behavior is to do nothing. /// Returns `null` if the behavior is to do nothing.
Simulation createScrollSimulation(T position, U velocity) => null; Simulation createScrollSimulation(T position, U velocity) => null;
/// Returns an animation that ends at the snap offset. /// Returns an animation that ends at the snap offset.
...@@ -26,7 +26,7 @@ abstract class ScrollBehavior<T, U> { ...@@ -26,7 +26,7 @@ abstract class ScrollBehavior<T, U> {
/// This function is called when a drag gesture ends and a /// This function is called when a drag gesture ends and a
/// [SnapOffsetCallback] is specified for the scrollable. /// [SnapOffsetCallback] is specified for the scrollable.
/// ///
/// Returns null if the behavior is to do nothing. /// Returns `null` if the behavior is to do nothing.
Simulation createSnapScrollSimulation(T startOffset, T endOffset, U startVelocity, U endVelocity) => null; Simulation createSnapScrollSimulation(T startOffset, T endOffset, U startVelocity, U endVelocity) => null;
/// Returns the scroll offset to use when the user attempts to scroll /// Returns the scroll offset to use when the user attempts to scroll
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert' show JSON, JsonEncoder; import 'dart:convert' show JSON, JsonEncoder;
import 'dart:math' as math;
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
...@@ -26,6 +27,8 @@ class TimelineSummary { ...@@ -26,6 +27,8 @@ class TimelineSummary {
/// Average amount of time spent per frame in the framework building widgets, /// Average amount of time spent per frame in the framework building widgets,
/// updating layout, painting and compositing. /// updating layout, painting and compositing.
///
/// Returns `null` if no frames were recorded.
double computeAverageFrameBuildTimeMillis() { double computeAverageFrameBuildTimeMillis() {
int totalBuildTimeMicros = 0; int totalBuildTimeMicros = 0;
int frameCount = 0; int frameCount = 0;
...@@ -40,6 +43,24 @@ class TimelineSummary { ...@@ -40,6 +43,24 @@ class TimelineSummary {
: null; : null;
} }
/// Find amount of time spent in the framework building widgets,
/// updating layout, painting and compositing on worst frame.
///
/// Returns `null` if no frames were recorded.
double computeWorstFrameBuildTimeMillis() {
int maxBuildTimeMicros = 0;
int frameCount = 0;
for (TimedEvent event in _extractBeginFrameEvents()) {
frameCount++;
maxBuildTimeMicros = math.max(maxBuildTimeMicros, event.duration.inMicroseconds);
}
return frameCount > 0
? maxBuildTimeMicros / 1000
: null;
}
/// The total number of frames recorded in the timeline. /// The total number of frames recorded in the timeline.
int countFrames() => _extractBeginFrameEvents().length; int countFrames() => _extractBeginFrameEvents().length;
...@@ -55,6 +76,7 @@ class TimelineSummary { ...@@ -55,6 +76,7 @@ class TimelineSummary {
Map<String, dynamic> get summaryJson { Map<String, dynamic> get summaryJson {
return <String, dynamic> { return <String, dynamic> {
'average_frame_build_time_millis': computeAverageFrameBuildTimeMillis(), 'average_frame_build_time_millis': computeAverageFrameBuildTimeMillis(),
'worst_frame_build_time_millis': computeWorstFrameBuildTimeMillis(),
'missed_frame_build_budget_count': computeMissedFrameBuildBudgetCount(), 'missed_frame_build_budget_count': computeMissedFrameBuildBudgetCount(),
'frame_count': countFrames(), 'frame_count': countFrames(),
'frame_build_times': _extractBeginFrameEvents() 'frame_build_times': _extractBeginFrameEvents()
......
...@@ -58,7 +58,7 @@ void main() { ...@@ -58,7 +58,7 @@ void main() {
end(1000), end(1000),
begin(2000), end(4000), begin(2000), end(4000),
]).computeAverageFrameBuildTimeMillis(), ]).computeAverageFrameBuildTimeMillis(),
2 2.0
); );
}); });
...@@ -68,7 +68,50 @@ void main() { ...@@ -68,7 +68,50 @@ void main() {
begin(2000), end(4000), begin(2000), end(4000),
begin(5000), begin(5000),
]).computeAverageFrameBuildTimeMillis(), ]).computeAverageFrameBuildTimeMillis(),
2 2.0
);
});
});
group('worst_frame_build_time_millis', () {
test('returns null when there is no data', () {
expect(summarize([]).computeWorstFrameBuildTimeMillis(), isNull);
});
test('computes worst frame build time in milliseconds', () {
expect(
summarize([
begin(1000), end(2000),
begin(3000), end(5000),
]).computeWorstFrameBuildTimeMillis(),
2.0
);
expect(
summarize([
begin(3000), end(5000),
begin(1000), end(2000),
]).computeWorstFrameBuildTimeMillis(),
2.0
);
});
test('skips leading "end" events', () {
expect(
summarize([
end(1000),
begin(2000), end(4000),
]).computeWorstFrameBuildTimeMillis(),
2.0
);
});
test('skips trailing "begin" events', () {
expect(
summarize([
begin(2000), end(4000),
begin(5000),
]).computeWorstFrameBuildTimeMillis(),
2.0
); );
}); });
}); });
...@@ -96,6 +139,7 @@ void main() { ...@@ -96,6 +139,7 @@ void main() {
]).summaryJson, ]).summaryJson,
{ {
'average_frame_build_time_millis': 7.0, 'average_frame_build_time_millis': 7.0,
'worst_frame_build_time_millis': 11.0,
'missed_frame_build_budget_count': 2, 'missed_frame_build_budget_count': 2,
'frame_count': 3, 'frame_count': 3,
'frame_build_times': <int>[9000, 1000, 11000], 'frame_build_times': <int>[9000, 1000, 11000],
...@@ -131,6 +175,7 @@ void main() { ...@@ -131,6 +175,7 @@ void main() {
await fs.file('/temp/test.timeline_summary.json').readAsString(); await fs.file('/temp/test.timeline_summary.json').readAsString();
expect(JSON.decode(written), { expect(JSON.decode(written), {
'average_frame_build_time_millis': 7.0, 'average_frame_build_time_millis': 7.0,
'worst_frame_build_time_millis': 11.0,
'missed_frame_build_budget_count': 2, 'missed_frame_build_budget_count': 2,
'frame_count': 3, 'frame_count': 3,
'frame_build_times': <int>[9000, 1000, 11000], 'frame_build_times': <int>[9000, 1000, 11000],
......
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