A few months ago, Mozilla began a project to make significant changes to our Gecko rendering engine to make it faster and more reliable. The project was just announced. In this post I will fill in some technical details. Quantum was originally conceived to integrate technology from our Servo research browser into Gecko. While the project has evolved somewhat since then, Servo has heavily influenced the design. Like Servo and Rust, the unifying themes of Quantum are increased concurrency and parallelism, reduced latency, and better reliability.
Quantum is roughly divided into four distinct projects.
The Quantum CSS project will replace Gecko’s CSS engine with the one from Servo. Servo’s engine is heavily parallel while Gecko’s is not.
The Quantum DOM project will make Gecko more responsive, especially when there are a lot of background tabs open. When Quantum DOM is finished, JS code for different tabs (and possibly different iframes) will run in separate cooperatively scheduled threads; the code for some background tabs will never run at all.
Quantum Compositor moves Gecko’s compositor into its own process. Since graphics driver instability is a major source of Firefox crashes, we expect that moving code that interacts with the GPU into its own process will make Firefox more stable.
Finally, Quantum Rendering will replace Gecko’s graphics subsystem with the one from Servo, called WebRender. Servo uses the GPU more effectively than Gecko does, driving it more like a game would than a browser.
These projects are in varying stages of completeness. Quantum Compositor is fairly far along while Quantum Rendering is just getting started. There’s still a good deal of uncertainty about the projects. However, I wanted to write about Quantum DOM, the project that I’m working on.
Quantum DOM is primarily designed to make web content run more smoothly and to reduce “jank”–annoying little hangs and hiccups that make for a poor browsing experience. A lot of jank comes from background tabs and advertisements. You can find measurements in bug 1296486. An easy way to reduce this jank is to run each tab as well as each frame within the tab in its own process. Since the OS schedules processes preemptively, background frames can always be interrupted to handle work in the foreground.
Unfortunately, increasing the number of content processes also increases memory usage. In some preliminary experiments we’ve done, it looks unlikely that we’ll be able to increase the number of content processes beyond 8 in the near future; any more processes will increase memory usage unacceptably. Eight content processes are certainly better than one, but many users keep more than 8 tabs open. Mozilla developers are working to reduce the memory overhead of a content process, but we will never be able to reduce the overhead to zero. So we’re exploring some new ideas, such as cooperative scheduling for tabs and tab freezing.
Quantum DOM is an alternative approach to reduce jank without increasing memory usage. Rather than putting frames in different processes, we will put them in separate threads. And rather than using OS threads, we will use cooperatively scheduled user-space threads. Threads are nice because they can share address space, which makes it much easier to share data. The downside of threads is that all this shared data needs to be protected with locks. Cooperative scheduling allows us to switch between threads only at “safe” points where the shared state is known to be consistent, making locks unnecessary (or much less necessary).
Ultimately, though, we want to run each frame in its own cooperatively scheduled thread. That will allow us to pause the execution of a background tab in order to process tasks (like input events or animations) in a foreground tab. Before we can do that, we need to “label” all the tasks in our event queue with the iframe that they correspond to. That way we can run each task on the thread that it belongs to.
Labeling tasks and prioritizing them is a big undertaking. Michael Layzell is building a “DocGroup” abstraction so that same-origin frames that can talk to each other, either via window.parent or window.opener, will run on the same thread (bug 1303196). Andreas Farre is implementing a mechanism to schedule low-priority tasks like GC (bug 1198381). Olli Pettay and Thinker Li are creating a similar mechanism for high-priority tasks like input events (bug 1306591). As our infrastructure for labeling improves over the next few weeks, task labeling will be a great place for contributors to help with the project. We’ll be posting updates to the dev-platform mailing list.
Eventually, we may want to run frames in their own preemptively scheduled OS threads. The advantage of OS threads over user-space threads is that they can take advantage of multiple cores. However, our user-space threads will still be split across 4 or 8 content processes, and most users don’t have more cores than that. As the project progresses, we will evaluate whether OS threads make sense for us.
As an addendum, I would be remiss if I didn’t point out that Opera pioneered a cooperatively scheduled architecture. They did all the good stuff first.
In our discussions about how to schedule background work, we began to wonder why we even run it at all. Firefox already throttles some background work, like setTimeout tasks, to run at most once per second. We’ll most likely do even more throttling in the future. But could we be even more aggressive and completely freeze certain background tabs?
Freezing isn’t always possible since some background tabs need to run. For example, you want Pandora to continue to play even when you switch away from it. And you want Gmail to notify you that an email has arrived even when the Gmail tab isn’t selected. But if the browser could identify such “important” tabs, we could freeze the rest. We’re still working out the heuristics that we’ll need to identify important background tabs. Ehsan Akhgari is doing some exploratory work and we’re hoping to land some experiments on Nightly soon.