Christopher L Merrill
©2006 Web Performance, Inc; January 15, 2006; v1.1
Being a performance company, we are always interested in the impact of new development techniques on the performance of web applications. We have numerous customers who have performance problems due primarily to the size of their web pages. Put another way - the pages are simply too big to achieve the desired performance goals with the available bandwidth. In some cases, the page consists primarily of content that is common between many pages. For instance, a header, footer and navigation menu that change infrequently, if at all, during the use of the application. This suggested that if the application was only updating the part of the page that needed to change, a considerable amount of bandwidth could be saved.
In order to test the theory, we decided to see if we could cut the bandwidth use of an application by at least 50%. We selected a rather simple internal data-analysis application. The application consists of a typical page layout with a central section containing the changing content. The header, footer and navigation menu do not change during the application operation. We modified the application so that it can be accessed in either traditional (page-refresh) mode or AJAX mode. We then used our measurement tool (Web Performance Load Tester) to record and analyze the bandwidth utilization of each application.
The first result of this effort surprised us a little. With all the talk of AJAX frameworks, we expected to have a difficult time choosing an appropriate AJAX framework to use in our application. After trying a few simple experiments with a few popular frameworks, and learning just enough Javascript to be dangerous, we settled on a simple collection of javascript functions to accomplish our goals. We were able to pull a few code snippets from various Javascript/AJAX tutorials around the 'net and with less than 100 lines of javascript (and some refactoring of our web-app) we converted the application to use AJAX. No frameworks needed.
The second result confirmed our expectation - we were able to cut the bandwidth usage of the application by 61%. The chart below shows some bandwidth statistics for the two versions of the application:
Scenario/mode | First-page size | Typical page size | Total bandwidth |
---|---|---|---|
Page-refresh | 44k | 10k | 210k |
AJAX | 47k | 2.5k | 81k |
TOTAL BANDWIDTH SAVINGS > 61%
Below are screenshots from our measurement tool (Web Performance Load Tester) showing the page/url transaction sizes for the original application (page-refresh mode) and the AJAX version. As you can see from the list of URLs and sizes below, the AJAX-mode application actually made the first page of the application larger. In our test, it was approximately 3k larger. This is not surprising, since this page contains the additional javascript required to drive the AJAX mode. If an AJAX framework had been chosen, we would expect this increase to be considerably larger.
Most notable is that the typical page size decreased from ~10k to ~2.5k – a reduction of 75%.
Figure 1 – Bandwidth requirements in page-refresh mode
Figure 2 - bandwidth requirements in AJAX mode
To achieve the bandwidth savings, we made a handful of modifications to our application.
First we added an application-mode switch. This used a context parameter in the web-application descriptor that the application could query to determine if it was in AJAX or page-refresh mode. Note that this step would be unnecessary in most applications.
Next we modified the HTML
form widgets to change the form-submission
mechanism. As an example, here is the starting tag for a SELECT
element
(drop-down list) before and after the modifications:
<SELECT name="type" onChange="window.document.theform.submit()"> <SELECT name="type" onChange="submitForm()">
The SELECT
element will now call a javascript function (see below) instead of using the browser to submit the form.
In order to mark the section of HTML that is to be replaced dynamically by the content returned from the server, it is placed inside a SPAN element named with an identifier we can use in our javascript functions:
<span id="content_area">
Next we wrote or scavenged several javascript functions to perform the AJAX-mode form submission and page update.
The first is the submitForm() method referenced in the SELECT elements (described above). This method is called to replace the browsers form-submission logic. The submission is accomplished in two steps:
function submitForm() { var content = convertFormDataToPostContent(window.document.theform); doPost('/office/UsageAnalyzer', content, 'processResult'); }
Look at the third parameter in the doPost()
method call: processResult
.
This is the name of our callback method. When the asynchronous method
is completed, this method will be called with the result.
The job of the processResult()
method (below) is to update the document with the result of the post. Note that the content_area
parameter to the getElementById()
method is the same as the id of the SPAN element we added to our HTML.
function processResult(result) { document.getElementById('content_area').innerHTML = result; }
The work of submitting the content in a POST transaction to the server is relatively simple. It creates a browser-specific request object, submits the content and creates a function used to perform the callback with the content returned by the server. This code was scavenged from the Internet and can be readily found in many AJAX articles or frameworks.
function doPost(url, content, callback_name) { var async_request = false; // Mozilla/Safari if (window.XMLHttpRequest) { async_request = new XMLHttpRequest(); async_request.overrideMimeType('text/xml'); } // IE else if (window.ActiveXObject) { async_request = new ActiveXObject("Microsoft.XMLHTTP"); } async_request.open('POST', url, true); async_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); async_request.onreadystatechange = function() { if (async_request.readyState == 4) { response_content = async_request.responseText; eval(callback_name + '(response_content);'); } } async_request.send(content); }
The form-field conversion method traverses the list of fields in the form and encodes them into the proper format for submission as a form-urlencoded post. Again, this code was scavenged from sources on the Internet.
function convertFormDataToPostContent(form_name) { var content_to_submit = ''; var form_element; var last_element_name = ''; for (i = 0; i < form_name.elements.length; i++) { form_element = form_name.elements[i]; switch (form_element.type) { // Text fields, hidden form elements case 'text': case 'hidden': case 'password': case 'textarea': case 'select-one': content_to_submit += form_element.name + '=' + escape(form_element.value) + '&' break; // Radio buttons case 'radio': if (form_element.checked) { content_to_submit += form_element.name + '=' + escape(form_element.value) + '&' } break; // Checkboxes case 'checkbox': if (form_element.checked) { // Continuing multiple, same-name checkboxes if (form_element.name == last_element_name) { // Strip of end ampersand if there is one if (content_to_submit.lastIndexOf('&') == content_to_submit.length - 1) { content_to_submit = content_to_submit.substr( 0, content_to_submit.length - 1); } // Append value as comma-delimited string content_to_submit += ',' + escape(form_element.value); } else { content_to_submit += form_element.name + '=' + escape(form_element.value); } content_to_submit += '&'; last_element_name = form_element.name; } break; } } // Remove trailing separator content_to_submit = content_to_submit.substr(0, content_to_submit.length - 1); return content_to_submit; }
In applications that have a significant part of each page containing content that is identical in multiple page requests, using AJAX-style methods to update only the relevant parts of a web page can bring significant bandwidth savings. Using less than 100 lines of javascript, we were able to quickly convert an existing web application to use AJAX page-update methods to drastically reduce (>60%) the bandwidth requirements of our sample application.
It is important to note that the application converted in our test was ridiculously simple. Achieving the same bandwidth reduction on a sophisticated application will likely not be as easy, if it is possible at all. However, when applied to extremely large-scale applications or applications with very tight bandwidth considerations, savings of 10% could bring a hefty cost savings.
It would be interesting to test a more realisitic application that has been converted from a previous version to use AJAX in the method described here. If you know of such an application, please contact us!
The impact on server CPU resources would also be an interesting study. However, given that none of the "fluff" on our pages requires any database queries or other processing work, our reference application may not be the best choice for this test.
Comments about this report may be posted at the company blog post.
v1.0 - 1st public release (15 jan 06)
v1.1 - email cleanup (23 Jan 09)