Software Secret Weapons™  
Javascript String Concatenation posted by Pavel Simakov on 2007-05-16 12:04:20 under AJAX
view comments
 


Introduction
Quick page loading and snappiness of a web site of a key factor to retaining visitors. Visitors do not like to wait! If you make visitors wait, they will go elsewhere. With raise of AJAX web applications it is quite tricky to make pages load fast. Many AJAX like application have to do lots of XML parsing and string manipulation to create the page in the browser. If not done carefully this can create an impression of web site being slow, even if server is lightning fast.

The Problem
As many other software performance problems the answer to the question why an application is slow can have complicated answer. And when the performance problem is in the web browser it is even harder to nail down. First of all, there are too many versions of different browsers out there. Second - browsers have limited debugging capabilities and can't always tell you what is wrong with them. Venkman Mozilla's JavaScript Debugger {1} is great for newer browsers, but Internet Explorer and many others does not have this kind of capability.

For the large part we have to rely on good practices, experimentation and gut feel. Among some general, but pretty useless tips on the Internet, I also found a great report by Dave Johnson {2} that compares performance for XML and JSON approaches to passing data. This report presents lots of important information for dealing with XML data in the web browser. But even after considering Dave's findings you can find you page slow to load. You might have parsed XML blazingly fast, but it might take too long to assemble HTML fragments from this XML. What to do in this case?

If you are building AJAX applications, the old trick of avoiding simple string concatenations is one of the most important tricks to learn. The problem is well known in all languages that support dynamic strings. Consider the JavaScript code sample in the Listing 1. It creates an HTML table using a variable to hold a body of the table.

Listing 1. Inefficient and slow JavaScript code for creating HTML table.

 
 
function slowMakeTable(buf){
	var COLS = 10;
	var ROWS = 500;
	var CELL = "0123456789"

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

	buf += "<table>";
	for (var i=0; i < ROWS; i++){
		buf += "<tr>";
		for (var j=0; j < COLS; j++){
			buf += "<td><i>" + CELL + "</i></td>";
		}
		buf += "</tr>";
	}
	buf += "</table>";

	var duration = new Date().getTime() - start;
	document.write("<br>" + (ROWS * COLS) + " cells - " + duration);

	return buf;
} 

Depending on the CPU speed of your computer and the browser running this code sample can take up to 5 seconds! And if you make a text in the cell longer, say 200 characters - this sample can take up to 45 seconds to complete! Main reason - there are over 5000 string concatenations in this example!

To measure precisely how much time string concatenations really takes up I have created a JavaScript function shown on Listing 2.

Listing 2. Test functions for measuring an impact of string concatenations on JavaScript performance.

 

function stringExpand(iter, inc){
	var buf = "";	
	for(var i=0; i < iter; i++){
		buf += inc;
	}
}


function test(){
	var WORD = "0123456789";
	var LOOPS = 5; 

	for (var i = 0; i < LOOPS; i++){
		var iter = i * 1000;
		var start = new Date().getTime();
		stringExpand(iter, WORD);	
		var duration = new Date().getTime() - start;
		document.write("<br>" + iter + " - " + duration);
	}
}

All this test does is concatenate strings. While doing so it varies number of concatenations and the size of the new string that is concatenated. The results are presented in Picture 1.

Picture 1.Performance impact of string concatenation in JavaScript. Tests performed on two browsers Internet Explorer 6.0.2800.1106.xpsp1 (labeled IE) and Mozilla/5.0 Gecko/20050511 Firefox/1.0 (labeled FF) on Pentium 4 CPU at 3 GHz. We ran test() function from Listing 2 while setting WORD variable to contain 1, 10, 100, 1000 characters. Different WORD size test cases are labeled on the picture as "word 1", "word 10", "word 100", and "word 1000" respectively.

As you can clearly see from the Picture 1, the Firefox handles string concatenations for small word size (between 1 and 10 characters) much better than Internet Explorer. Both browsers behave about the same when we start using large word size from 100 to a 1000. They both take huge amounts of time.

The Solutions
There is no surprise here whatsoever. The same problem exists in Java language for example. All JSP developers are advised to avoid String class and += operator and use StringBuffer instead. There is no StringBuffer in JavaScript, but the lessons can be learned.

There are two key steps to minimize string concatenation problem:

  • minimize number of concatenations (so there is no need to reallocate memory)
  • minimize the size of the string that is being appended to (so there is less memory to reallocate)

The improved version of HTML table generator is presented on Listing 3. The changes are very small. Instead of using one buffer buf for all string concatenations, we use additional intermediate buffer row in the inner loop. This revised example of HTML table generator takes less than 0.1 s to run! So we have improved things at least 50 times.

Listing 3. Efficient and fast JavaScript code for creating HTML table.

 
 
function fastMakeTable(buf){
	var COLS = 10;
	var ROWS = 500;
	var CELL = "0123456789"

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

	buf += "<table>";
	for (var i=0; i < ROWS; i++){
		var row = "<tr>";
		for (var j=0; j < COLS; j++){
			row += "<td><i>" + CELL + "</i></td>";
		}
		row += "</tr>";
		buf += row
	}
	buf += "</table>";

	var duration = new Date().getTime() - start;
	document.write("<br>" + (ROWS * COLS) + " cells - " + duration);

	return buf;
}

Huge performance boost can be obtained by simply using intermediate strings! It is possible to create StringBuffer-like class in JavaScript to gain even more performance boost. When I posted a link to this article on comp.lang.javascript one of the readers (Lasse Reichstein Nielsen) provided almost complete implementation for a JavaScript StringBuffer. Here it is:


 function StringBuffer() { 
   this.buffer = []; 
 } 

 StringBuffer.prototype.append = function append(string) { 
   this.buffer.push(string); 
   return this; 
 }; 

 StringBuffer.prototype.toString = function toString() { 
   return this.buffer.join(""); 
 }; 

 var buf = new StringBuffer();

 buf.append("hello");
 buf.append("world");

 alert(buf.toString());

Reader's Feedback

  • Tue May 15 21:36:12 EDT 2007
    Hi, I test your codes both in IE 7.0 and Firefox / 2.0.0.3. With IE what you said is correct, the fastMakeTable is much faster. But with Firefox, the 2 functions run at almost the same speed. slowMakeTable() is even a little faster.

References

  1. Venkman JavaScript Debugger
  2. JavaScript Benchmarking IV: JSON Revisited

Comments (4)

  • Comment by Álvaro G. Vicario — October 19, 2007 @ 7:31 am

    Take into account that the suggested StringBuffer implementation is merely a proof of concept. It works fine in Explorer but in Firefox it’s awfully slow when actually converting to string (toString method).

  • Comment by Mark — October 29, 2007 @ 1:18 am

    I was thinking to doing same benchmark. I just discovered this yesterday doing AJAX data fetches for a google map. I though it was the Google map running slow on IE, but turned out it was the string concats during JSON parsing. 2 MINUTES on IE7 vs 2 SECONDS on FF to parse a string element a few thousand chars long. Have they never heard of pascal style strings, with a length a field so you don’t have to walk the whole damn string to find the end if you want to append a char?

    Kudos to Google for getting maps to work so well on IE. If I could wish the IE codebase to just vanish of the face of the earth, I’d do it in a second. Don’t they realize the extreme ill will they have created in the developer community? It will be the eventual death of them. </rant>

  • Comment by Andrew — April 15, 2008 @ 12:12 am

    I know this is an old post…but still quite relevant. I happened to look at the source of this site: bell.ca
    There appear to be hundreds of lines of JS (analytics code?) that are concatenated like this:
    +”d.s_c_in++;s.m=function(m){return (”+m).indexOf(’{')<0};s.fl=funct”
    +”ion(x,l){return x?(”+x).substring(0,l):x};s.co=function(o){if(!o)r”
    +”eturn o;var n=new Object,x;for(x in o)if(x.indexOf(’select’)<0&&x.i”
    I assume it gets eval’d somewhere and is simply an obfuscation technique…but wouldn’t this code perform horribly based on the data you show here?

  • Comment by khuram — May 15, 2008 @ 9:12 am

    fghdfhgfh dhgfhdfh hdgh fgh


Leave a comment


  Copyright © 2004-2007 by Pavel Simakov SourceForge.net Logo