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!