Sunday, September 30, 2012

Boost jquery datatables performance

I have been working on a project which included the display of 2000 rows of data which is at the end not a very high number. My preferred javascript library for tables is jquery datatables. Since the project server side component is ASP.NET MVC the easiest way to configure the final rendering of the HTML would be to generate the HTML with ASP and than when the page is loaded to call the datatables initializer on it.
In the latest firefox (version 15) this took 10 seconds to finish. Not acceptable of course. This split into 50-50 for server-side and client-side processing. Server side processing could be dramatically reduced by the following optimizations:
  1. Reduce the amout of database queries by adding Include("Related objects") in the entity framework initial query for the objects and related objects of interest in a lazy loading setup
  2. I created the table using type reflection to be more flexible on changing object properties and table display.
Client side optimization was a bit more tricky to resolve. The call to the dataTables() function which already included 2000 rows with 5 columns each needed ~5 seconds to return. This was mainly the tradeoff for analyzing all the data and building the internal datastructures for sorting and filtering. So in order to prevent heavy dom operations for building the internal datastructure it would be obvious to go the other way around by handing over the data as json and building the html from the datasource. This reduced the time needed to a not mentionable amount in firefox. So i happily used the mRender property for the creation of a bit advanced table content like links and one div with content. The bad awakening happend after testing other browsers, everything seemed fine, just the old lady Internet Explorer 8 (i haven't tested in 7 but it could only be worse) needed full 10 seconds for the creation of the whole table.

The process of data in the dataTables plugin is basically to create each and every single row and cell when the table is initilized. While thinking about it i only wanted to see 15, 30 or 50 rows of content at a time, since for 2000 rows you do need pagination anyhow. Forunately i was able to assign a function to mRender which allows you to seperate the content for display and sorting of the cell. But this did not lead to the wished result since the pagination was brought to datatables via a plugin the initial library is not aware of any functionality like that. Therefore the only way to achieve that is to use the rowCallback of the dataTables which is called whenever a row is shown. This allowed me to basically empty the table cells (at least the ones with a bit more than just text inside of them) and create the more complex content only at the point in time the table row is actually shown. Which at the end looks something like:

"aoColumns": [
{
    mRender: function (data, type, full) {
               if (type == "display")
                   return '';
               return data[0];
             }
         }

} ],
"fnRowCallback": function (tr, data, index) {
   var tds = $(tr).find("td");

   if (tds.children().length == 0) {
       tds.eq(0).html(

'<a href="' + data[0] + "'><img src="globe.png" /></a>'
       );
   }
}

Now the content of the cell is converted into a link represented by an image at the moment it is added to the dom and not when the table initializes. This reduced the processing time in IE8 to less than 1 second and therefore boosts the performance by more than factor 10.

--- edit 28.12.2012
The datatables do also offer a function for deferred rendering which is partly also achieving this optimization. The problem with this function is that the sorting and filtering operations are not working properly, at least in my tests.

2 comments:

Unknown said...

Nice fix! Helped me a lot.

Anonymous said...

Awesome article.