Menu

Tuning AJAX

November 30, 2005

Dave Johnson

Unless you live under a rock, you've heard about and likely even used AJAX. Asynchronous JavaScript and XML is becoming an increasingly pervasive deployment methodology, which necessitates that people start to both understand how it works and actually consider it more seriously as an enterprise-level development tool. To that end, I will try to illustrate one method of benchmarking your AJAX applications as well as point out some of the major performance pitfalls I have encountered while developing AJAX components and applications.

Web Browser Mechanics

When working with AJAX, it is paramount to understand that not only are there many different ways to update a web page but they also have varying levels of performance between browsers. Equally important to understand is that no matter how you update a web page, when you make changes to its (X)HTML content the parsing and rendering engine in the browser needs to update its internal representation of the page (recalculating the flow and layout) and then render the changes to the browser window -- for complex pages or changes this can take a considerable amount of time. MSDN's DHTML Performance Tips is a good overview of DHTML performance.

Benchmarking Basics

In JavaScript the easiest way to benchmark one's code is by using the Date object. The Date object has a very handy method called getTime(), which returns the number of milliseconds that have passed since January 1, 1970. This is useful for doing any date arithmetic, like timing how quickly your code is running, and it forms the basis from which you can benchmark your AJAX applications. For example:

var date = new Date();

var start = date.getTime();

//    do stuff here.

var end = date.getTime();

//    notify the user

alert(end - start);

The first nuance that developers generally run into here is that the getTime() method returns the date in milliseconds but often either the operation is simply too fast (and maybe not worth worrying about then) or the browser will tend to return factors of ten making anything under 10ms hard to measure. Both this, and the fact that the results tend to vary between tests, means that it is good practice to repeat your tests several times to get an average and standard deviation.

Another limitation of JavaScript benchmarking is that if tests take a considerable amount of time the browser will usually request that the script be aborted. This can be circumvented to some extent by using the setTimeout() function to initiate any loops of code. Using setTimeout() also lets the browser update any changes to the user interface such as debugging or process information. An example using setTimeout() is shown here:

//    global variables

var g_time = [];

var g_iteration = 0;

var g_maxIterations = 0;



function DoTest()

{

var start = new Date().getTime();

//    do stuff here.

var end = new Date().getTime();

//    save time

g_time.push(end - start);

if (g_iteration > g_maxIterations)

    return;

g_iteration++;

setTimeout(DoTest);

}

Using getTime() is a good way to go about benchmarking during development or any exploratory work. However, when it comes to testing a product it can be very useful to apply something more substantial such as the Venkman Debugger for Mozilla-based browsers or one of the many available commercial profilers (Tito JavaScript Profiler or Whitefrost JavaScript Profiler).

Before AJAX, it was generally accepted that most tasks performed in JavaScript would be small enough that developers needn't really worry about performance issues. On the other hand, in today's world of AJAX maps and photo sharing this can be a critical mistake. Having said that, I don't expect everyone to run off and scour their JavaScript in search of potential optimizations that will shave a few milliseconds off their "yellow-fade." JavaScript is generally fast enough that you can build your program and after refactoring any obvious bottlenecks it should run fairly quickly.

JavaScript

Since we are talking AJAX, I have to give a little time to JavaScript, which is the glue of the AJAX world. JavaScript is largely uncharted territory for many developers, and many have little idea about its inner workings, let alone how it changes across browsers -- which it can do dramatically. When dealing with an interpreted language like JavaScript, it always helps to remember the simple things like making expensive function calls inside loops and being aware of the complexity of various algorithms.

At the same time, there are things that developers should try to forget simply because JavaScript is a very different beast. In many OOP languages inheritance and member accessors are commonplace, but in JavaScript these can be expensive operations. Conventional object declaration in JavaScript, by specifying new Object() for example, can be slow compared to using the slightly less versatile anonymous object syntax as shown below. This is the approach that the popular Prototype AJAX framework takes.

Rather than

var Foo = new Object(); 

Foo.a = "100";

Foo.b = "200";

consider

Foo = { 

    a: "100",

    b: "200"

}

When accessing values of objects it is also advisable to use the associative array notation (since every object in JavaScript is an associative array) rather than the property name:

alert(Foo.a);

versus

alert(Foo['a']);

XML

There are two other very important areas that need some attention when discussing AJAX performance. Asynchronous indicates that there is some sort of messaging aspect to AJAX and this depends on both the network and the application data; this is closely related to the X in AJAX, which is the ubiquitous XML data or document format. Network performance and data are intimately related, and this is one point where people like to claim the relatively verbose nature of conventional XML messages as a reason for not using XML in AJAX. But there are always more or less compact XML representations of domain- or application-specific data structures. In many cases the XML in your application can be redesigned to be easier to process. XML is a reasonable option for your AJAX application when dealing with large amounts of data that need to be filtered or sorted real-time in the browser using XSLT.

XSLT

If you are using XML data in your AJAX applications you are likely going to use XSLT. For intranets and the enterprise, AJAX applications may need to deal with SOAP or at least XML-based web services, in which case XSLT is a great fit. There are a few areas where XSLT can be optimized for AJAX. The first is actually in how you write your style sheet and is applicable to any XSLT processor. If possible, you should write explicit XSLT templates rather than data-driven ones; by explicit I mean using <xsl:apply-templates /> sparingly and instead opting for <xsl:for-each /> elements to match nodes. It often takes the XSL processor longer to find the templates rather than simply looping explicitly, and this method has the added benefit of slightly reducing the XSLT file size. Certainly the fewer wildcards such as * or // that you use in your XPath queries the faster the transformations will be. Also, using the <xsl:key /> tag and the key() function to create name-value pairs for lookups in complex XML documents can have considerable performance advantages.

The one problem with XSLT is that it has widely varying performance between browsers and in particular between Internet Explorer and any Mozilla-based browser. In IE 6, XSLT is far faster than JavaScript, whereas the opposite is true (to a lesser degree) in Mozilla browsers (see JavaScript/AJAX Benchmarking I).

Given that XSLT in IE is very fast, it is sometimes a good option for doing even mundane operations like formatting numbers using the format-number() function. Furthermore, the recommended method of using XSLT in IE 6 is to use the XSLTemplate object rather than the familiar DOMDocument. The XSLTemplate object compiles and caches the XSLT style sheet, which speeds up future transformations (see MSDN's Increasing Performance by Using the XSLTemplate Object).

Of course if you want your AJAX application to support non-XSLT browsers like Opera, then XSLT might not be the best idea. Sadly even Google's goog-ajaxslt isn't a fast XSLT processor implemented in JavaScript.

JSON

No discussion of AJAX performance would be complete without mentioning JavaScript Object Notation (JSON) since it not only is a very popular alternative to XML but also can be very fast in Mozilla browsers where the JavaScript engine outpaces the XSLT processor considerably.

Data represented in JSON is relatively legible for humans and relatively cheap for JavaScript engines to parse; and it can be instantiated simply by calling the JavaScript eval() method and passing your JSON string as a parameter. Keep in mind that using eval() to do anything tends to be relatively slow. Also, running eval() with JavaScript code that was received from an untrusted source can be a security problem since it can send any JavaScript it wants. To get around this there's JSON JavaScript parser; however, no matter what browser you are using, it is painfully slow. JSON excels in situations where only a few JavaScript objects are being passed from the server to the client and no client-side filtering or sorting needs to be done -- objects can be very quickly and easily instantiated and used within your program context.

DOM and CSS

Interacting with the DOM and changing CSS values can also be time-consuming operations. Both of these require that the browser re-layout and re-render the page. There are also some tags, such as <table>, which tend to be very slow when using any special DOM methods (insertRow() and insertCell() for <table> tags), changing style properties or accessing calculated properties (offsetWidth, offsetLeft, etc.). If you do have to use DOM methods to manipulate nodes such as createElement() and appendChild(), ensure that you do all DOM manipulations offline -- after which use appendChild() or replaceNode() to get the generated nodes into the document. Of course, if you really want to insert (X)HTML into your document quickly, the nonstandard node property innerHTML is the fastest way to go.

Developers are often faced with situations where a group of nodes, say in a list or grid, need the same formatting applied. In those situations one can either loop through all the nodes and set the style properties or class names, or alternatively, access the CSS class directly by using the document.styleSheets[index].rules collection. This, of course, depends greatly on the number of nodes that need to be looped through and the types of style being applied. There are also more unconventional, yet high-performance ways of applying some styles such as background colors. A background color can be simulated by placing an element with a given constant background color behind the element to which you want the background color applied. In both IE 6 and Firefox (Windows) this method is actually faster than setting the background color through CSS.

AJAX Compromise

In AJAX applications, performance is undoubtedly important. Even so, as with any engineering discipline there are compromises to be made. End users will have different operating systems and browsers, applications will have various amounts of code or data to download and process, and there will be different business problems that AJAX can help solve. You need to consider the OS/browser statistics of the target user, and design with performance on those systems in mind. Also, both code and data size have a large effect not only on processing time but also on network latency, which has led to important AJAX design patterns such as pre-fetching of data. The bottom line is that in a commercial environment it is always prudent to let your end users and the business case for your AJAX application direct your choices. Whether you are a single person building an open source AJAX combo box or a small company trying to compete with large AJAX vendors, you need to consider who the end users are and how they use the product.

Writing fast JavaScript isn't always obvious. There are few steadfast rules, and developers need to be vigilant in their performance benchmarking as well as in how they write code. I like to think writing enterprise AJAX applications is a lot like writing assembly code for a real-time system; to make it fast you generally have to step outside of the OOP best practices paradigm and do what works, not what makes your code easy to read and maintain. This flies in the face of some primary virtues of software engineering. Eventually these techniques will mature and become encapsulated in server-side logic and declarative markup where they can be better managed and leveraged. Until then, we'll have to keep on finding new and faster ways of tuning AJAX.