performance

Speed Up Table Rendering in Ember by Preventing Layout Thrashing

When implementing an infinite scroll table, we really want users to feel a smooth scrolling experience when rendering new table rows. There are many reasons that can cause a table to stutter. One that is often overlooked is layout thrashing. In this article, we will showcase an issue we encountered and the solution we found while developing PubNation.com with Ember.

The Table and the Issue

The table is simple, just scroll to the bottom and it will load data and render more rows. When we were implementing the design, we felt the scrolling speed was a little bit off. It’s not very obvious, but we pulled out Chrome Dev Tool -> Timeline and did a recording while we were scrolling.

We noticed that the frame rate was particularly low while loading data. Part of this was expected since we are dumping 25 records onto the DOM at the same time. It also made us wonder which part is particularly slow. We zoomed in around XHR calls, and a line of purple squares popped into our eyes.

It seems like we have forced our browser to read and write from the DOM repetitively in a short period of time. After checking out source code on the left, I found this line in the table row component.

didInsertElement: function () {
    ...
    this.set('enableTooltip', this.element.scrollWidth > this.element.clientWidth);
}

So we were querying the dimension of the element for each row when the row got inserted into DOM. I remembered seeing an article about Layout Thrasing. When we insert into the DOM, if we read a layout attribute immediately, the browser will become very busy calculating the up-to-date value of that attribute, which leads to adding a lot more computation to an operation that’s already very heavy.

The Solution

When I looked at this enableTooltip property, I asked myself “do we need to know it now”? The answer is “No” in this case. So I turned it into an on-demand computed property:

enableTooltip: computed(function () {
    ...
    return this.element.scrollWidth > this.element.clientWidth;
})

So this way, we didn’t have to query the DOM when it’s busy inserting our data. I did a new recording of the timeline:

And the frame rate did go above 60FPS most of the time. The scrolling felt smoother. Looking at our XHR calls again:

It’s a lot cleaner this time. Cheers! (Although we might have hit the wall with some garbage collection problem, but that’s another blog post :P) We successfully offloaded the browser from repetitively computing the layout and users could enjoy a smoother table.

Conclusion

By working on this table scroll issue, we learned a few things:

  1. Modern browsers are not exempt from performance concerns. Sometimes simple operations can have large impact on performance if not treated carefully.
  2. It’s not hard to solve performance issues. Debugging performance with Chrome Dev Tools is not scary at all.
  3. When manipulating DOM, batching operations together is a fast and simple way to ensure performance.

Let us know if you have any tips or experiences to share for speeding up Ember!

Inspecting the Web Inspector

Chrome's immensely useful Web Inspector has a timeline feature that displays, among other information, when certain events happen in a page's lifecycle. These events are represented by tiny vertical stripes on the timeline's x-axis.

Timeline event stripes

The blue and red events are the DOMContentLoaded and Load events, but what about the green and orange ones? I couldn't find any reference to them online. And to be fair, they don't seem to be available in the stable version of Chrome. I'm not sure in what version they were added, but they're certainly present in Version 33.0.1750.5 dev and above, because that's where I see them.

I asked in #chrome-devtools if anyone knew what the green and orange events represented, and someone came up with the brilliant idea of inspecting the inspector:

  1. Press Command+Option+i (Ctrl+Shift+i on Windows) to open DevTools.

  2. Make sure that the developer tools are undocked into a new window. You may have to click or long-hold the docking icon in the bottom left: Undock icon

  3. Press Command+Option+i again on this new window.

  4. That will open the DevTools on the DevTools. You can redock the page's DevTools if you want.

  5. If it's not already, select Elements — it's the first icon at the top of the inspector.

Once you do that, you can see that these event stripes have title attributes. Unfortunately, the events are covered by other UI elements, so mousing over them does not display the titles as tooltips, as it should.

Event stripe title attribute

As expected:

  • resources-blue-divider is "DOMContentLoaded event"
  • resources-red-divider is "Load event"

And the unknowns?

  • resources-green-divider is "First paint"
  • resources-orange-divider is "CSI/tbsd_"

First paint is a useful event: it will strongly affect a user's perception of a page's speed. Good to know! (I wonder, though, is the green stripe really the first painting that happens? On the timeline, it generally appears after some painting activity has taken place, but perhaps it represents the first appearance of pixels on the screen or something like that.)

So what on earth is "CSI/tbsd_"?

Googling led me to a couple of not so useful results.

Paul Irish chimed in to answer this one:

<paul_irish> wjb: csi?tbsd is a console.time call comng from google
<wjb> paul_irish: thanks!
<wjb> paul_irish: Why does a console.time timer name end up as the title of a timeline event?
<paul_irish> because.
<paul_irish> people end up using it for "structured profiling" like that more than they do to just get millisecond counts

So it's a convenient shorthand for the Chrome team to profile something in the page lifecycle — who knows what. So far, it only seems to appear on pages with Google WebFonts, so it might be related to loading those.