Bringing you a snappier Firefox

In this blog post we’ll talk about what the Firefox Performance team set out to achieve for 2021 and the Firefox 89 release last month. With the help of many people from across the Firefox organization, we delivered a 10-30% snappier, more instantaneous Firefox experience. That’s right, it isn’t just you! Firefox is faster, and we have the numbers to prove it.

Some of the things you might find giving you a snappier response are:

  • Typing in the URL bar or a document editor (like Google Docs or Office 365)
  • Opening a site menu like the file menu on Google Docs
  • Playing a browser based video game and using your keyboard to control your movements within the video

Our Goals

We have made many page-load and startup performance improvements over the last couple of years which have made Firefox measurably faster. But we hadn’t spent much time looking at how quickly the browser responds to little user interactions like typing in a search bar or changing tabs. Little things can add up, and we want to deliver the best performance for every user experience. So, we decided it was time to focus our efforts on responsiveness performance starting with the Firefox June release.

Responsiveness

The meaning of the word responsiveness as used within computer applications can be rather broad, so for the purpose of this blogpost, we will define three types of experiences that can impact the responsive feel of a browser.

  1. Instantaneous responsiveness: These are simple actions taken by a user where the browser ought to respond instantly. An example is pressing a key on your keyboard in a document or an input field. You want these to be displayed as swiftly as possible, giving the user a sense of instantaneous feedback. In general this means we want the results for these interactions to be displayed within 50ms[1] of the user taking an action.
  2. Small but perceptible lag: This is an interaction where the response is not instantaneous and there is enough work involved that it is not expected to be. The lag is sufficient for the user to perceive it, even if they are not distracted from the task at hand. This is something like switching between channels on Slack or selecting an email in Gmail. A typical threshold for this would be that these interactions occur in under a second.
  3. Jank: This is when a site, or in the worst case the browser UI itself, actually becomes unresponsive to user input for a non-insignificant amount of time. These are disruptive and perceptible pauses in interaction with the browser.

Instantaneous Responsiveness

We’ve had some pretty solid indications (from tests and proofs of concepts) that we could make our already fast interactions feel even more instantaneous. One area in particular that stood out to us was the ‘depth’ of our painting pipeline.

About our painting pipeline

Let’s talk about that a little more. Here you can see a graphical representation of our old painting pipeline:

Most users currently use 60Hz monitors, which means their monitor displays 60 frames per second. Each segment of the timeline above represents a single frame on the screen and is approximately 16.67ms.

In most cases, when input is received from the operating system, it would take anywhere from 0 to 16.67ms for the next frame to occur (1), and at the start of that new frame, we would paint the resulting changes to the UI (the green rectangle).

Then another 16.67ms later (2), we would composite the results of that drawing onto the browser window, which would then be handed off to the OS (the blue rectangle).

At the earliest, it would be another 16.67ms later (3) when the operating system would actually present the result of the user interaction (input) to the user. This means even in the ideal case it would take at least 34-50ms for the result of an interaction to show up on the screen. But often there is other work the browser might be doing, or additional latency introduced by the input devices, the operating system, or the display hardware that would make this response even slower.

Shortening the Painting Pipeline

We set out to improve that situation by shortening the painting pipeline and by better scheduling when we handle input events. Some of the first results of this work were landed by Matt Woodrow, where he implemented a suggestion by Markus Stange in bug 1675614. This essentially changed the painting pipeline like this:

We now paint as soon as we finish processing user input if we believe we have enough time to finish before the next frame comes in. This means we can then composite the results one frame earlier. So in most cases, this will result in the frame being seen a whole 16.67ms earlier by the users. We’ll talk some more about the results of this work later.

Currently this solution doesn’t always work, for example if there are other animations happening. In Firefox 91, we’re bringing this to more situations as well as improvements to the scheduling of our input event handling. We’ll be sure to talk more about those in future blog posts!

Small but perceptible lag

When it comes to small but perceptible lags in interaction, we found that the majority of lags are caused by time spent in JavaScript code. Much has been written about why JavaScript engines in their current form are often optimized for the wrong thing. TL;DR – Years of optimizing for benchmarks have driven a lot of decisions inside the engine that do not match well with real world web applications and frameworks like React.

Real World JavaScript Performance

In order to address this, we’ve begun to closely investigate commonly used websites in an attempt to understand where our browser may be under-performing on these workloads. Many different experiments fell out of this, and several of these have turned into promising improvements to SpiderMonkey, Firefox’s JavaScript engine. (The SpiderMonkey team has their own blog, where you can follow along with some of the work they’re doing.) One result of these experiments was an improvement to array iterators (bug 1699851), which gave us a surprise improvement in the Firefox June Release.

Along with this, many other ideas were prototyped and implemented for future versions. From improvements to the architecture of object structures to faster for-of loops, the JavaScript team has contributed much of their time to offering significant improvements to real world JS performance that are setting us up for plenty of further improvements to come throughout the rest of 2021. We’d especially like to thank Ted Campbell, Iain Ireland, Steve Fink, Jan de Mooij and Denis Palmeiro for their many contributions!

Jank

Through hard work from Florian Quèze and Doug Thayer, we now have a Background Hang Reporter tool that helps us detect (and ultimately fix) browser jank.

Background Hang Reporter

While still in the early stages of development, this tool has already proven extremely useful. The resulting data can be found here. This essentially makes it possible for us to see the stacktraces of frequently seen main thread hangs inside the Firefox parent process. We can also attach bugs to these hangs in the tool, and this has already helped us address some important issues.

For example, we discovered that accessibility was being enabled unnecessarily for most Windows users with a touchscreen. In order to facilitate accessibility features, the browser does considerable extra work. While this extra work is critical to our many users that require these accessibility features, it caused considerable jank for many users that did not need them. James Teh’s assistance was invaluable in resolving this, and with the landing of bug 1687535, the number of users with accessibility code unnecessarily enabled, as well as the number of associated hang reports, has gone down considerably.

Measuring performance

Along with all this work, we’ve also been in the process of attempting to do better at measuring the performance for our users ‘in the wild’, as we like to say. This means adding more telemetry probes that collect data about how your browser is performing in an anonymous way, without compromising your privacy. This allows us to detect improvements with an accuracy that no form of internal testing can really provide.

Improved “instantaneous responsiveness”

As an example we can look at the latency for our keyboard interactions. This describes the time taken between the operating system delivering us a keyboard event, to us handing the frame off to the window manager:

If we take into account an additional frame the OS requires to display our change, 34ms is approximately the time required to hit the 50ms “instantaneous” threshold,). Looking at the 28-35ms bucket, we see that we now hit that target more than 40% of the time, vs less than 30% in Firefox 86.

Improved “Small but perceptible lag”

Another datapoint we can look at actually tells us more about the speed of our JS processing, this describes the time from when we receive an input event from the operating system, to when we’ve processed the JavaScript handler associated with that input event.

If we look carefully here we can see a small, but consistent shift here from the higher buckets to the lower buckets. We’ve actually been able to track this improvement down and it appears to have occurred right around the landing of bug 1699851. Since we had not been able to detect this improvement internally outside of microbenchmarks, it reaffirms the value of improving our telemetry further to better determine how our work is impacting real users.

What’s Next?

We’d like to again thank all the people (including those we might have forgotten!) that contributed to these efforts. All the work described above is still in its early days, and all the improvements we’ve shown here are the result of first steps taken as a part of more extensive plans to improve Firefox responsiveness.

So if you feel Firefox is more responsive, it isn’t just your imagination, and more importantly, we’ve got more speedups coming!


[1] Research varies widely on this so we choose 50ms as a latency threshold which is imperceptible to most users.

No comments yet

Comments are closed, but trackbacks are open.