Performance/Fenix/Best Practices: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
No edit summary
Line 5: Line 5:


== Think carefully before optimizations using background threads ==
== Think carefully before optimizations using background threads ==
Concurrency is complex so be careful before introducing it! Even getting the thread pooling correct is challenging. TODO
Concurrency is complex so be careful before introducing it! Even choosing a threading strategy that does not have side effects on the system can be challenging. TODO


== Dispatchers.Default is not a good default ==
== Dispatchers.Default is not a good default ==

Revision as of 23:08, 11 May 2021

This guide will help Fenix developers working on front-end code (i.e. Kotlin/Java) produce code which is as performant as possible – not just on its own, but in terms of its impact on other parts of Firefox. Always keep in mind the side effects your changes may have, from blocking other tasks, to interfering with other user interface elements.

Avoid blocking on the main thread

TODO

Think carefully before optimizations using background threads

Concurrency is complex so be careful before introducing it! Even choosing a threading strategy that does not have side effects on the system can be challenging. TODO

Dispatchers.Default is not a good default

When a task needs to be done on a background thread but it's not an IO operation, it frequently gets added to the default coroutine dispatcher, Dispatchers.Default. However, this is an anti-pattern: if the default dispatcher's task queue is full, newly added tasks must wait for earlier tasks to finish before executing. This is a problem if your task needs to return a result (to the user/UI) quickly and the running tasks are slow: for example, if the user clicked a button and you want to show them content in response, they may end up waiting on unrelated tasks. Note: this problem is exacerbated on 2-core devices where only two tasks can execute simultaneously.

In general, Dispatchers.Default should only be used for computationally intensive tasks where tasks are FIFO priority or equal priority (this doesn't come up much in Android). It'd be much less misleading if it were named Dispatchers.Computation.

As for what to do if the default dispatcher is not a good fit, there's no one-size-fits-all solution. To choose the right threading strategy, it's important to understand your options: learn what each of the Dispatchers are intended for, what mechanisms they use to manage that, and their implications. Here are some additional considerations:

  • Dispatchers.IO is often a much better default choice than the default dispatcher because it will usually re-use threads and generally execute tasks immediately. However, if your code produces many simultaneous tasks, the IO dispatcher may not be a good fit because it could create numerous threads and cause the dispatcher to hit its cap, causing new tasks to wait on earlier tasks
  • Avoid creating a dedicated thread or thread pool unless it's strictly necessary: each new thread costs the system resources

If you have questions about what the appropriate threading strategy is, please ask the perf team!