Posted by podlipensky on September 22 7:42 AM

While working on big and complex web applications you’ll be faced to performance issues sooner or later. Its just a matter of time. So one of my recent issues was related to slowdown of the app in Chrome in one particular case. Chrome is well known for it’s V8-super-duper-fast engine and I was surprised that Firefox works faster in that case.

The case I’m talking about is processing large amount of big strings. Don’t want to dig into app-specific functionality, so let’s go ahead with the only knowledge that we have to do a lot of substring operations on very long strings in JavaScript.

Later I implemented simple test which reflects my real-world-code in order to investigate the difference:

function doTest(){ 
    var chars = []; 
    var n = 1024 * 1024;//number of chars in our string, so it will allocate 1024 * 1024 * 16 bits = 1024 * 1024 * 2 byte = 2048 kB 
    var i = 0; 
    while(i < n) { 
     chars.push(String.fromCharCode(32 + Math.floor(Math.random() * 91))); 
     i++; 
    } 
    var s = chars.join(''); //making the Big string      
    i = 0; 
    var start = (new Date).getTime(); 
    while(i < 10000){ 
      var c = n / 2; //get string of the half size of initial string size 
      var r = s.substring(s.length - c, s.length);//copy from c to the end of the string 
      i++; 
    } 
    var diff = (new Date).getTime() - start; 
    output('Diff: ' + diff);//Chrome - 3606, FF - 2 
} 
function output(s){ 
  document.write('You\'re using ' + BrowserDetect.browser + ' ' + BrowserDetect.version + ' on ' + BrowserDetect.OS + '</br>'); 
  document.write(s); 
}

Note: I’ve used simple BrowserDetect script from quirksmode in my test.

So I’ve generated 1024*1024 random characters and combined them into single long long string. After that I’m using very primitive approach to measure execution time of 10000 substring operations, and my results are:

Chrome 14.0.835.186 – 3606ms

Firefox 6.0.2 – 2ms

Surprised? So did I.

The explanation is simple: in the case of Firefox (with its engine named Spidermonkey), a substring() call just creates a new “dependent string” - a string object that stores a pointer to the original string and the start and end offsets. This is precisely to make substring() fast, and is an obvious optimization given immutable strings.

But why Chrome (with its V8 engine) does not do thatjQuery152018068479001522064_1316645491390? Probably this is because V8 is trying to save space. In the “dependent string” setup if you hold on to the substring but forget the original string, the original string can't get GCed because the substring is using part of its string data.

Anyway, I just took a look at the V8 source, an it looks like they just don't do any sort of “dependent strings” at all; the comments don't explain why they'd don't, though.

In order to confirm my words I did additional tests to measure memory consumption by both browsers and here the results:

Chrome memory usage before test

image

Chrome memory usage after test

image

As you may note memory consumption is low because all the strings were GCed after the test.

I’ve used different tool named Heap Profiler for Firefox as they don’t provide such stat as a Chrome:

image

image

Spidermonkey’s garbage collector needs more time to figure out “dependent strings” references and eliminate the tree of strings from the memory, so we see 2.5MB of memory allocation right after test execution.

Try out my substring test page.

Related Articles

Diving into memory usage with Heap Profiler

Mozilla Performance Tools

JSPerf: Substrings

blog comments powered by Disqus