Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I statically allocate Javascript strings for memory performance?

So I'm writing this node.js application and I'm trying to make it super fast and with a low memory footprint. I've got a lot of string concatenation going on, functions like:

function f(pt) { 
    return pt.x + ' + ' + pt.y; 
}

If I do this like 100 million times right on the inner loop of my application, does the Javascript engine allocate and have to free that string ' + ' 100 million times? Would it be more performant memory-wise to rewrite this code as something like

var plus = ' + ';
function f(pt) { 
    return pt.x + plus + pt.y; 
}

Or does the compiler just do that in the background anyway, or does it not even matter? (My code is really using much longer strings than ' + ', I just used that as an example.)

like image 902
Inquisitive Idiot Avatar asked Mar 12 '15 09:03

Inquisitive Idiot


3 Answers

It depends.

But honestly, it might be a bit slower.
This is because it has to search for the variable plus within the function scope, then the window object and then on the scope above to find it.

So, I believe it might be slower.

Consider the following code:

console.time('plus outside');
var plus=' x ', fn=function(pt){return pt.x + plus + pt.y};
for(var i=0; i<1e5; i++){ fn({x:5,y:6}); }
console.timeEnd('plus outside');

And this code:

console.time('plus inside');
var fn=function(pt){return pt.x + ' + ' + pt.y};
for(var i=0; i<1e5; i++){ fn({x:5,y:6}); }
console.timeEnd('plus inside');

Running both on Google Chrome v41.0.2272.89 m, plus outside took 200ms while the inside took 175ms!
That is 25% faster!

Below, you can see a printscreen with roughly the same times:

chrome console

You can test it on your computer too!

window.onload=function(){

	(function(elem){
		
		var plus=' + ',fn=function(pt){return pt.x + plus + pt.y}, start=new Date();
      
		for(var i=0; i<1.5e7; i++){ fn({x:5,y:6}); };
		var end=new Date();
		
		elem.innerHTML=(end-start)+'ms (i:'+i+')';
		
	})(document.getElementById('plus_outside'));
  
  
  
	
	(function(elem){
		
		var fn=function(pt){return pt.x + ' + ' + pt.y}, start=new Date();
      
		for(var i=0; i<1.5e7; i++){ fn({x:5,y:6}); };
		var end=new Date();
		
		elem.innerHTML=(end-start)+'ms (i:'+i+')';
		
	})(document.getElementById('plus_inside'));
  
  
	
	(function(elem){
		
		var fn=function(pt){return [pt.x,'+',pt.y].join(' ')}, start=new Date();
      

		for(var i=0; i<2e5; i++){ fn({x:5,y:6}); };
		var end=new Date();
		
		elem.innerHTML=(end-start)+'ms (i:'+i+')';
		
	})(document.getElementById('array'));
  
  
  	(function(elem){
		
		var fn=function(pt){return [pt.x,' + ',pt.y].join('')}, start=new Date();
      

		for(var i=0; i<2e5; i++){ fn({x:5,y:6}); };
		var end=new Date();
		
		elem.innerHTML=(end-start)+'ms (i:'+i+')';
		
	})(document.getElementById('array_nojoin'));
  
  
  
   (function(elem){
		
		var fn=function(pt){return ([pt.x,'+',pt.y]+'').replace(',',' ')}, start=new Date();
      

		for(var i=0; i<2e5; i++){ fn({x:5,y:6}); };
		var end=new Date();
		
		elem.innerHTML=(end-start)+'ms (i:'+i+')';
		
	})(document.getElementById('array_replace'));
  

};
<font face="sans-serif">
<p>
  Plus inside: <span id="plus_inside"></span><br>
  &nbsp;&nbsp;fn: <code>function(pt){return pt.x + ' + ' + pt.y}</code>
</p>

<p>
  Plus outside: <span id="plus_outside"></span><br>
  &nbsp;&nbsp;fn: <code>function(pt){return pt.x + plus + pt.y}</code>
</p>

<p>
  Array: <span id="array"></span><br>
  &nbsp;&nbsp;fn: <code>function(pt){return [pt.x,'+',pt.y].join(' ')}</code>
</p>

<p>
  Array (no join): <span id="array_nojoin"></span><br>
  &nbsp;&nbsp;fn: <code>function(pt){return [pt.x,' + ',pt.y].join('')}</code>
</p>


<p>
  Array (replace comas): <span id="array_replace"></span><br>
  &nbsp;&nbsp;fn: <code>function(pt){return ([pt.x,'+',pt.y]+'').replace(',',' ')}</code>
</p>
</font>
like image 116
Ismael Miguel Avatar answered Oct 22 '22 05:10

Ismael Miguel


String concatenation can be speeded up by pushing the strings onto an array. And then joining the array.

["a","b","c"].join('');

This is because join is one call and also the compiler can calculate the full length of the resulting string immediately.

like image 37
QuentinUK Avatar answered Oct 22 '22 06:10

QuentinUK


This would be implementation dependent, but at least as far as V8 (Chrome) goes, no, each function call does not appear to instantiate a new ' + '. Most likely the function body is compiled into an operation that does the concatenation in one shot.

You can see this by first defining a function (in the console):

var f = function(a, b) { return a + ' + ' + b; };

Then starting up the heap profiler and executing this:

var c = f('1', '2');

What the profiler shows is that only a single string is allocated during this time:

"1 + 2"

What this seems mean is that it's not even treating the two separate concatenation operations as separate operations. It's doing it all in one shot.

The bottom line: Micro-optimizations like this are not likely to get you far. As Ismael indicates, it might actually slow down your code.

like image 45
JLRishe Avatar answered Oct 22 '22 07:10

JLRishe