Skip to content

Fireperf: encapsulate FrameMetricsAggregator in a new object called FrameMetricsRecorder #3665

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 27, 2022

Conversation

leotianlizhan
Copy link
Contributor

@leotianlizhan leotianlizhan commented Apr 20, 2022

b/229123892

TLDR

Add a new class called FrameMetricsRecorder that is essentially an encapsulation of FrameMetricsAggregator. Every activity will have an instance of FrameMetricsRecorder.

Timeline Graph to Understand The Relationship between Activity, Fragments, and FrameMetricsAggregator

This an example of what is happening on a timeline. Fragment can be ANY UI object that is meaningful. e.g.) a listview that caused performance problems before, want to have screen traces for it to catch when such happens again in the future.
Android SDK Screen Rendering Design Proposal (1)

Background

Following the stopping screen trace in onActivityPostPaused change and realizing that is unviable due to onActivityPostPause being only available in API 29+, a new design is needed for screen rendering tracing.

Why encapsulate FMA

There's 2 main reasons:

  1. Relieve some responsibility from AppStateMonitor, a class with too much responsibility
  2. Structurally prevent FMA misuse in the future by anyone working on Fireperf Android SDK

Classes like AppStateMonitor are very heavy with many responsibilities: manage FrameMetricsAggregator, instrument, create, and build proto for screen trace and _fs/_bs traces, etc. All which are on top of its responsibility of registering itself for activity lifecycle callbacks, which is already a lot of boilerplate to read through. This makes it hard to test and debug.

Massive “god classes” like AppStateMonitor are also error prone and any small change could break it. FrameMetricsAggregator is an example of an intricate class that requires special knowledge to handle, and that is only one responsibility in a very bloated class AppStateMonitor. To debug the Activity screen trace bug, you need to read through other code that is unrelated to screen traces, which is adding a lot more mental load to figure out how everything interacts with each other.

Responsibility of FrameMetricsRecorder

A manager for FrameMetricsAggregator, includes FMA instantiation, start activity recording, stop activity recording, calculating differences in FrameMetricsAggregator’s data for a sub-trace (e.g. Fragment trace). Basically it only has a single responsibility: abstract away FrameMetricsAggregator and anything related to it.

Design Rationale

Structurally enforces only 1 activity is being passed into each FrameMetricsAggregator.

Structurally prevent misuse of FrameMetricsAggregator

  • By abstracting away all that relates to FrameMetricsAggregator, guards can be put in place to prevent misuse.
    e.g.) in start(), can check if FMA is already recording, if it is, don’t allow it.
  • Detecting misuse then throwing errors helps surfacing Inter-Activity Behavior and Screen Trace, otherwise this bug was difficult to find.

Simplify code and encapsulate responsibility

  • Inputs and outputs use PerfFrameMetrics, FMA is completely contained and encapsulated within this class.
  • AppStateMonitor doesn't manage FrameMetricsAggregator, it simply calls start() and stop().
  • Each method has only a single purpose, easy to test unlike many big multi-purpose methods in other classes.
// AppStateMonitor.java
private void onActivityCreated(Activity a) {
  FrameMetricsRecorder recorder = new FrameMetricsRecorder(a);
  recorderMap.put(a, recorder);
  …
}
private void onActivityStarted(Activity a) {
  recorderMap.get(a).start();
  …
  // create a duration trace…
  …  
}
private void onActivityStopped(Activity a) {
  PerfFrameMetrics data = recorderMap.get(a).stop()
  …
  // add data to duration trace then send it
  …  
}

Structure

Android SDK Screen Rendering Design Proposal

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Apr 20, 2022

Coverage Report 1

Affected Products

  • firebase-perf

    Overall coverage changed from 71.36% (51cc21f) to 71.44% (1c86195) by +0.08%.

    FilenameBase (51cc21f)Merge (1c86195)Diff
    FrameMetricsCalculator.java96.30%96.77%+0.48%
    FrameMetricsRecorder.java?77.14%?

Test Logs

Notes

  • Commit (1c86195) is created by Prow via merging PR base commit (51cc21f) and head commit (27300b9).
  • Run gradle <product>:checkCoverage to produce HTML coverage reports locally. After gradle commands finished, report files can be found under <product-build-dir>/reports/jacoco/.

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/Qaj5HZKdY9.html

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Apr 20, 2022

Size Report 1

Affected Products

  • firebase-perf

    TypeBase (51cc21f)Merge (1c86195)Diff
    aar308 kB311 kB+2.80 kB (+0.9%)
    apk (release)2.47 MB2.47 MB+892 B (+0.0%)

Test Logs

Notes

  • Commit (1c86195) is created by Prow via merging PR base commit (51cc21f) and head commit (27300b9).

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/cA5FA9J3tE.html

@leotianlizhan leotianlizhan requested a review from jposuna April 20, 2022 21:56
Copy link
Contributor

@jposuna jposuna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before reviewing the changes themselves, can you explain why we want to have this?

@jeremyjiang-dev
Copy link
Contributor

Could you mark this PR to ready for review and I can take another look?
Would you like to merge this PR to a feature branch instead of master for all the frame metrics refactor work?

@leotianlizhan leotianlizhan changed the base branch from master to perf-fix-fma April 22, 2022 01:34
@leotianlizhan
Copy link
Contributor Author

Could you mark this PR to ready for review and I can take another look? Would you like to merge this PR to a feature branch instead of master for all the frame metrics refactor work?

Done. I changed base branch to a new feature branch

@leotianlizhan
Copy link
Contributor Author

/retest

@leotianlizhan leotianlizhan requested a review from jposuna April 26, 2022 00:02
@leotianlizhan
Copy link
Contributor Author

/retest


/** Starts recording FrameMetrics for the activity window. */
public void start() {
if (isRecording) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also expose an API isRecordingFrameMetricsForActivity(Activity activity)? This would ensure that the caller knows the current state before starting to track an activity.

Copy link
Contributor Author

@leotianlizhan leotianlizhan Apr 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what value that API would add because I don't see us using it. We are just going to call start() in onActivityStart and stop() in onActivityStop, if it fails then we are not doing symmetrical calls and we are calling start() somewhere else, which is wrong and it should output warning.

In addition I don't think we should expose internal state, that's opening the blackbox. if start() stop() are called wrong and causes internal state to be problematic, that's a problem that should be contained in the blackbox, all the outside has to know is that "I used this black box in the wrong way".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to Leo

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm mixed on this. I have seen usecases where having such an API would be very useful. But in the interest of self-containment, I'm fine to move ahead here.

Copy link
Contributor

@jposuna jposuna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this and spending the time to make the changes!

Copy link
Contributor

@jeremyjiang-dev jeremyjiang-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Leo for your hard work! Great work!

@leotianlizhan leotianlizhan merged commit c76d022 into perf-fix-fma Apr 27, 2022
@leotianlizhan leotianlizhan deleted the perf-fma-abstraction branch April 27, 2022 18:28
@firebase firebase locked and limited conversation to collaborators May 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants