With performance tuning, what is the best way to invoke JavaScript methods several times?

I studied JavaScript performance. I learned that when accessing more than once, it is best to copy the closure variables and class members to the local scope in order to speed things up. For example:

var i = 100; var doSomething = function () { var localI = i; // do something with localI a bunch of times var obj = { a: 100 }; var objA = obj.a; // do something with objA a bunch of times }; 

I understand it; it adds a shortcut to the interpreter, which searches for the value by name. This concept becomes very obscure when considering methods. At first I thought it would work the same way. For example:

 var obj = { fn: function () { // Do something return this.value; }, value: 100 }; var objFn = obj.fn objFn(); // call objFn a bunch of times 

However, this will not work. Access to a similar method removes it from the scope. When it reaches the line this.value, this refers to the window object, and this.value is likely to be undefined. Instead of directly calling objFn and losing the area, I could pass its volume back to it with objFn.call (obj), but does it do better or worse than the original obj.fn ()?

I decided to write a script to test this, and I got very confusing results. This script iterates through several tests that repeatedly scrolls the above function. The average time spent on each test is displayed on the body.

An object is created using many simple methods. Additional methods are there to determine if the interpreter should work much harder to find a specific method.

Test 1 just calls this.a (); Test 2 creates a local variable a = this.a then calls a.call (this); Test 3 creates a local variable using the YUI binding function to preserve the area. I commented on this. Additional function calls created by YUI make this slower.

Tests 4, 5, and 6 are copies of 1, 2, 3, except for using z instead.

The YUI later function is used to prevent runaway script errors. Timing is done in real testing methods, so setTimeouts should not affect the results. Each function is called 10,000,000 times. (Easy to configure if you want to run tests.)

Here is my entire XHTML document that I used for testing.

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xml:lang="en" dir="ltr"> <head> <script type="text/javascript" src="http://yui.yahooapis.com/combo?3.1.2/build/yui/yui-min.js"></script> <script> YUI().use('node', function (Y) { var o = { value: '', a: function () { this.value += 'a'; }, b: function () { this.value += 'b'; }, c: function () { this.value += 'c'; }, d: function () { this.value += 'd'; }, e: function () { this.value += 'e'; }, f: function () { this.value += 'f'; }, g: function () { this.value += 'g'; }, h: function () { this.value += 'h'; }, i: function () { this.value += 'i'; }, j: function () { this.value += 'j'; }, k: function () { this.value += 'k'; }, l: function () { this.value += 'l'; }, m: function () { this.value += 'm'; }, n: function () { this.value += 'n'; }, o: function () { this.value += 'o'; }, p: function () { this.value += 'p'; }, q: function () { this.value += 'q'; }, r: function () { this.value += 'r'; }, s: function () { this.value += 's'; }, t: function () { this.value += 't'; }, u: function () { this.value += 'u'; }, v: function () { this.value += 'v'; }, w: function () { this.value += 'w'; }, x: function () { this.value += 'x'; }, y: function () { this.value += 'y'; }, z: function () { this.value += 'z'; }, reset: function () { this.value = ''; }, test1: function (length) { var time = new Date().getTime(); while ((length -= 1)) { this.a(); } return new Date().getTime() - time; }, test2: function (length) { var a = this.a, time = new Date().getTime(); while ((length -= 1)) { a.call(this); } return new Date().getTime() - time; }, test3: function (length) { var a = Y.bind(this.a, this), time = new Date().getTime(); while ((length -= 1)) { a(); } return new Date().getTime() - time; }, test4: function (length) { var time = new Date().getTime(); while ((length -= 1)) { this.z(); } return new Date().getTime() - time; }, test5: function (length) { var z = this.z, time = new Date().getTime(); while ((length -= 1)) { z.call(this); } return new Date().getTime() - time; }, test6: function (length) { var z = Y.bind(this.z, this), time = new Date().getTime(); while ((length -= 1)) { z(); } return new Date().getTime() - time; } }, iterations = 100, iteration = iterations, length = 100000, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, body = Y.one('body'); body.set('innerHTML', '<span>Running ' + iterations + ' Iterations&hellip;</span>'); while ((iteration -= 1)) { Y.later(1, null, function (iteration) { Y.later(1, null, function () { o.reset(); t1 += o.test1(length); }); Y.later(1, null, function () { o.reset(); t2 += o.test2(length); }); /*Y.later(1, null, function () { o.reset(); t3 += o.test3(length); });*/ Y.later(1, null, function () { o.reset(); t4 += o.test4(length); }); Y.later(1, null, function () { o.reset(); t5 += o.test5(length); }); /*Y.later(1, null, function () { o.reset(); t6 += o.test6(length); });*/ if (iteration === 1) { Y.later(10, null, function () { t1 /= iterations; t2 /= iterations; //t3 /= iterations; t4 /= iterations; t5 /= iterations; //t6 /= iterations; //body.set('innerHTML', '<dl><dt>Test 1: this.a();</dt><dd>' + t1 + '</dd><dt>Test 2: a.call(this);</dt><dd>' + t2 + '</dd><dt>Test 3: a();</dt><dd>' + t3 + '</dd><dt>Test 4: this.z();</dt><dd>' + t4 + '</dd><dt>Test 5: z.call(this);</dt><dd>' + t5 + '</dd><dt>Test 6: z();</dt><dd>' + t6 + '</dd></dl>'); body.set('innerHTML', '<dl><dt>Test 1: this.a();</dt><dd>' + t1 + '</dd><dt>Test 2: a.call(this);</dt><dd>' + t2 + '</dd><dt>Test 4: this.z();</dt><dd>' + t4 + '</dd><dt>Test 5: z.call(this);</dt><dd>' + t5 + '</dd></dl>'); }); } }, iteration); } }); </script> </head> <body> </body> </html> 

I ran this using Windows 7 in three different browsers. These results are in milliseconds.

Firefox 3.6.8

 Test 1: this.a(); 9.23 Test 2: a.call(this); 9.67 Test 4: this.z(); 9.2 Test 5: z.call(this); 9.61 

Chrome 7.0.503.0

 Test 1: this.a(); 5.25 Test 2: a.call(this); 4.66 Test 4: this.z(); 3.71 Test 5: z.call(this); 4.15 

Internet Explorer 8

 Test 1: this.a(); 168.2 Test 2: a.call(this); 197.94 Test 4: this.z(); 169.6 Test 5: z.call(this); 199.02 

Firefox and Internet Explorer yielded results on how I expected. Test 1 and Test 4 are relatively close, Test 2 and Test 5 are relatively close, and Test 2 and Test 5 take longer than Test 1 and Test 4 because there is an additional function call for processing.

I don’t understand Chrome at all, but it’s much faster, maybe tuning the submillisecond performance is not needed.

Does anyone have a good explanation for the results? What is the best way to call JavaScript methods multiple times?

+6
performance javascript object methods call
source share
3 answers

Just theorize, so take it with salt ...

The Chrome Javascript engine, V8, uses an optimization method called Hidden Classes. It basically creates static objects, which are Javascript shadow dynamic objects, where each property / method maps to a fixed memory address that can be referenced immediately without requiring the expensive table lookup operation. Each time a Javascript object has an added / removed property, a new hidden class is created.

My theory of the results of your test with Chrome is that a function reference in a free local variable breaks the relationships of a hidden class. Although referencing local variables probably also does not require a table lookup, you now need to perform an additional step when reassigning the 'this' variable. For a method in a hidden class, 'this' is a fixed value, so it can be called without this step.

Again just theorized. It might be worthwhile to test the comparison of differences between local variable references vs object.member in Chrome to make sure the performance rating for the latter is significantly lower than in other browsers, presumably due to hidden classes.

+2
source share

Well, if your site has IE8 users as visitors, it doesn’t matter at all. Use 1 or 3 (users will not notice the difference).

There is probably no good answer to the question "why." When it comes to optimization, these script engines are more likely to focus on optimizing the scenarios that they see happen in real life, where optimization can be proven to work correctly, and where it matters and that way that this invalidates the least amount of testing.

+1
source share

As in general, everything that you do is unlikely to change the situation if you do not know , he is responsible for a lot of time. (“Significantly,” I mean a significant percentage.)

Here is an easy way to find out which code is taking a lot of time.

+1
source share

All Articles