Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript profiling mystery - closure variables

I was testing performance (with chrome timeline) on cases if variable defined inside a closure. So it's values would not be exposed to user.

As expected run_proto_fn run few times faster and with minimal garbage collections, and low memory heap.

But run_proto_obj happened to make exact opposite, as if it was costly having non-function values at object prototype property properties.

Can someone share some clarity here?

SOME = function(){};
SOME.prototype.exe = function(v){
	var x = {
		a:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
		b:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
		c:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
	};
	return x[v];
};

SOME2 = function(){};
SOME2.prototype.exe = function(v){
	return this.exes[v];
};
SOME2.prototype.exes = {
	a:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
	b:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
	c:'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea, quae repudiandae eveniet cumque consequatur vitae aut. Nisi perspiciatis magnam explicabo optio reprehenderit dignissimos at porro quam, neque dolorum, architecto odit?',
};
SOME_FN = function(){};
SOME_FN.prototype.exe = function(v){
	var x = {
		a: function(p){this.p1 = p;return this;},
        b:function(p){this.p2 = p*3;return this;},
        c:function(p){this.p3 = p*99;return this;},
    };
  return x[v].call(this,42);
};

SOME_FN2 = function(){};
SOME_FN2.prototype.exe = function(v){
    return this.exes[v].call(this,42);	
};
SOME_FN2.prototype.exes = {
    a: function(p){this.p1 = p;return this;},
    b:function(p){this.p2 = p*3;return this;},
    c:function(p){this.p3 = p*99;return this;},
};

var a1 = a2 = a_fn1 = a_fn2 = [];
	

var run_local_obj = function(){
	for (var i = 1000000 - 1; i >= 0; i--) {
		x1 = new SOME();
		x1.exe('a');
		a1.push(x1);
	}
};
var run_proto_obj = function(){
	for (var i = 1000000 - 1; i >= 0; i--) {
		x2 = new SOME2();
		x2.exe('a');
		a2.push(x2);
	}
};
var run_local_fn = function(){
	for (var i = 1000000 - 1; i >= 0; i--) {
		x1 = new SOME_FN();
		x1.exe('a');
		x1.exe('b');
		x1.exe('c');
		a_fn1.push(x1);
	}
};
var run_proto_fn = function(){
	for (var i = 1000000 - 1; i >= 0; i--) {
		x2 = new SOME_FN2();
		x2.exe('a');
		x2.exe('b');
		x2.exe('c');
		a_fn2.push(x2);
	}
};
<button onclick="run_local_obj(this)">local obj</button>
<button onclick="run_proto_obj(this)">proto obj</button>
<button onclick="run_local_fn(this)">local obj FN</button>
<button onclick="run_proto_fn(this)">proto obj</button>

I have heard a phrase:

closure variable is defined each time that function runs

but still, i'ts foggy.

like image 491
animaacija Avatar asked Feb 02 '16 23:02

animaacija


1 Answers

I tried to run the measurements and first what I noticed is that it is hard to understand what is going on just using the code from your question.

If I run these tests one-by-one (clicking at the buttons and looking at the timeline) then for each next run the results differ much. From few runs it looked like the third (run_local_fn) is usually longer than others.

Then I tried to run them in the backward order (click buttons from 4 to 1) and got the completely different result - the run_local_obj was the longest.

So I modified the test code a bit, to be able to get stable results.

Full code is here, you can clone the repo and test it locally.

Here is how I run each test in Chrome (I launched it using python -m SimpleHTTPServer 8082 inside the js-perf-test folder):

  • Open the tab with http://localhost:8082/perf.html, open dev tools
  • Start the timeline recording
  • Click the button
  • Wait for results in console (min/max/mean)
  • Stop the timeline recording

Results are stable and repeatable (both timings and timeline view), here is one of tests:

  • `run_local_obj' - 508.45ms, timeline
  • `run_proto_obj' - 433.11ms, timeline
  • `run_local_fn' - 756.26ms, timeline
  • `run_proto_fn' - 560.62ms, timeline

So what do we see here is:

1) The run_proto_obj is the fastest and run_local_obj is close to it.

I think it is expected for run_proto_obj to be most efficient since it uses statically defined object with strings. And JS engine is probably able to optimize the run_local_obj, so the x object is re-used and not created each time.

2) The run_local_fn is the slowest.

Here we have the local x object and dynamic strings calculation, more floating parts than in other tests.

3) The run_proto_fn is faster than run_local_fn, but slower than first two functions.

I think this is also expected, the object with a, b, c is defined only once (so it is faster than run_local_fn).

And comparing to the first two functions, it calculates the resulting strings dynamically, so it is slower.

So back to your question:

As expected run_proto_fn run few times faster and with minimal garbage collections, and low memory heap. But run_proto_obj happened to make exact opposite, as if it was costly having non-function values at object prototype property properties.

It looks for me like you just didn't setup the experiment properly. According to the results above, run_proto_obj is actually the fastest.

Update: I just tried the same test in Firefox, results are similar:

  • `run_local_obj' - 344.85ms
  • `run_proto_obj' - 151.47ms
  • `run_local_fn' - 788.08ms
  • `run_proto_fn' - 265.21ms

The run_proto_obj is the fastest and run_local_fn is the slowest.

like image 105
Boris Serebrov Avatar answered Oct 04 '22 16:10

Boris Serebrov