Fast CSS3


These are my notes from a talk at SXSW 2012

Primary goal: Partner should be able to refer traffic from collateral
Secondary goal: Convert good enough to drive PPC

Content tree/DOM tree vs Frame tree/Rendering Tree

The act of constructing the rendering tree from the content tree/dom tree is a huge cost. After it's constructed, we have to compute all the positions. We call this process layout or reflow, where we assign a position and size to all the elements in the rendering tree. Once we do that, we can paint these objects using a 2d graphics api, and this is the part of the browser which we'll focus on.

This is a sensible concept for how you render a static page. But what if the pages are dynamic? Many pages today change the dom and styles and so on. We need to see how the browser responds to those dynamic changes. Do we change the DOM and then do everything over? That won't work for a lot of things. For example, if the change is as a result of the user typing, we won't be able to keep up with the typing. You want the page to respond quickly to all kinds of user interactions. There are three main types of optimizations.

First, the browser can sometimes skip entire steps of this process. Sometimes, the browser can skip only part of a step. And lastly, the browser can coalesce changes. Remember that optimizations can and do change, but there are a lot of common elements to browser performance. A single browser can change the way they optimize things. By and large, we try to make things faster, but that's not always the case... sometimes, we may come up with an optimization that makes some important cases faster, while other cases become slower.

Skipping steps

  1. Content
  2. Compute style
  3. Construct frames
  4. Layout
  5. Paint

If we have a script that sets an attribute that's not inherently presentational, we want to make sure it doesn't change the presentation. If we can verify this, we can change the content and then quit the step. Perhaps we'll change an attribute and re-run selector matching but don't see any changes; we'll also exit.

Sometimes you don't have to recompute nodes, but you need to recompute positions and sizes:

  • width
  • height
  • font-*
  • margin-*
  • padding-*
  • border-*-width
  • letter-spacing
  • word-spacing
  • line-height

Sometimes we don't have to redo layout, but we still need to redraw:

  • color
  • background-*
  • border-*-color
  • z-index

Sometimes we use custom optimizations, because we want them to be fast or because it's custom handling:

  • transform
  • cursor

Coalescing changes

We want to coalesce changes because authors do things that would require us to do the same work multiple times. For example, if the browser sets the position and overflow properties, we don't want to re-run the entire process twice, we want to merge the changes before we do the reconstruction or repaint.

So when do we actually make these changes? We don't want to process the changes until:

  • It's time to redraw
  • A script asks for something (e.g. sizes, style) that requires processing those changes

As a result, you shouldn't, for example, access a property that will cause a flush in a loop. Causing a flush each time the loop steps will slow down your code dramatically.

Skipping part of a step

Reconstructing a frame implies reconstructing all of its descendants. If you do something that forces a reconstruct on the body element, you're reconstructing the entire tree. Fortunately, reconstructing the rendering tree is proportional to the number of things you're constructing (unless the tree is very deep.)

If you want to see how heavy part of a page is, you can test it using code that intentionally does what we discussed not to do, which is to run a loop and refresh the page a thousand times with a timer running in the background.

  • When layout of one element changes, it can move others around.
  • Reflow/re-layout runs from the top of the tree down to the things it needs to change.
  • Recomputing intrinsic widths is slightly separate; there are two different phases to doing layout.
  • Some properties force relayout of all descendants, some don't.
  • Cost of layout depends on surrounding elements, so experiment with different surroundings of your elements.

Did you enjoy this post? Please spread the word.