Performance/Fenix: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
(Add defense for expensive APIs)
(→‎Defense: discouraging use of expensive APIs: Reorganize and add runtime block old API)
Line 3: Line 3:
== Defense: discouraging use of expensive APIs ==
== Defense: discouraging use of expensive APIs ==
In some cases, we want to discourage folks from using expensive APIs such as <code>runBlocking</code>. As a first draft solution, we propose a multi-step check:
In some cases, we want to discourage folks from using expensive APIs such as <code>runBlocking</code>. As a first draft solution, we propose a multi-step check:
# Compile-time check throughout the codebase: write a code ownered test asserting the number of references to the API.
# **Compile-time check throughout the codebase:** write a code ownered test asserting the number of references to the API.
## ''Question: given the lint rule, should we just count the number of `@Suppress` for this?''
## ''Question: given the lint rule, should we just count the number of `@Suppress` for this?''
## ''Question: would it help if this was an annotation processor on our lint rule and we look for <code>@Suppress</code>?''
## ''Question: would it help if this was an annotation processor on our lint rule and we look for <code>@Suppress</code>?''
# Run-time check on critical paths: wrap the API and increment a counter each time it is called. For each critical path (e.g. start up, page load), write a code ownered test asserting the number of calls to the API.
## **Add lint rule to discourage use of the API.** This overlaps with the compile-time check, however:
### We can't just use the compile-time check because in the best case it'll only run before the git push – it won't appear in the IDE – and the feedback loop will be too long for devs
### We can't just use the lint rule because it can be suppressed and we won't notice
# **Run-time check on critical paths:** wrap the API and increment a counter each time it is called. For each critical path (e.g. start up, page load), write a code ownered test asserting the number of calls to the API.
## ''Question: is this too "perfect is the enemy of the good?"''
## ''Question: is this too "perfect is the enemy of the good?"''
# Add lint rule to discourage use of the API. This overlaps with the compile-time check, however:
## **If you're doing this on a built-in API, you'll need to ban use of the old API e.g. with ktlint rule since it's harder to suppress**
## We can't just use the compile-time check because in the best case it'll only run before the git push – it won't appear in the IDE – and the feedback loop will be too long for devs
## We can't just use the lint rule because it can be suppressed and we won't notice


== App start up ==
== App start up ==

Revision as of 20:39, 13 October 2020

Performance can be thought in terms of "Offense" – the changes that you make to actively improve performance – and "Defense" – the systems you have in place to prevent performance regression's (this offense/defense idea from this blog post).

Defense: discouraging use of expensive APIs

In some cases, we want to discourage folks from using expensive APIs such as runBlocking. As a first draft solution, we propose a multi-step check:

  1. **Compile-time check throughout the codebase:** write a code ownered test asserting the number of references to the API.
    1. Question: given the lint rule, should we just count the number of `@Suppress` for this?
    2. Question: would it help if this was an annotation processor on our lint rule and we look for @Suppress?
    3. **Add lint rule to discourage use of the API.** This overlaps with the compile-time check, however:
      1. We can't just use the compile-time check because in the best case it'll only run before the git push – it won't appear in the IDE – and the feedback loop will be too long for devs
      2. We can't just use the lint rule because it can be suppressed and we won't notice
  2. **Run-time check on critical paths:** wrap the API and increment a counter each time it is called. For each critical path (e.g. start up, page load), write a code ownered test asserting the number of calls to the API.
    1. Question: is this too "perfect is the enemy of the good?"
    2. **If you're doing this on a built-in API, you'll need to ban use of the old API e.g. with ktlint rule since it's harder to suppress**

App start up

Defense

The FE perf team has the following measures in place to prevent regressions:

  • Show long term start up trends with Nightly performance tests (note: these are not granular enough to practically identify & fix regressions)
  • Prevent main thread IO by:
    • Crashing on main thread IO in debug builds using StrictMode (code)
    • Preventing StrictMode suppressions by running tests that assert for the current known suppression count. We are Code Owners for the tests so we will have a discussion if the count changes (code)
  • Code added to the start up path should be reviewed by the performance team:
    • We are Code Owners for a few files

WIP

We're working on adding:

  • Regression testing per master merge (issue)
  • Prevent main thread IO with:
    • Static analysis to prevent runBlocking, which can circumvent StrictMode (issue)
  • Code added to the start-up path should be reviewed by the performance team:
    • We're investigating other files that can be separated so we can be code owners for the start up parts (issue)
  • Minimize component initialization with:
    • Avoid unnecessary initialization (issue)
  • Prevent unnecessarily expensive UI with:
    • NestedConstraintLayout static analysis (issue)

Offense

TODO: improve this section, if useful (let us know if it is)

We're keeping a list of the biggest known performance improvements we can make. Also, we have a startup improvement plan.