⬆️ ⬇️

JS optimization killers are no longer so scary

A year ago, I saw the translation of Optimization Killer , and was surprised at how much I had to keep in my head to write optimized js code. It was especially frustrating that almost all es6 fell under deoptimization.





And here's a new optimizer in v8, called TurboFan, over the past year has learned how to optimize this most practically all es6, es5 and even try-catch is no longer a problem.



class TestClass { megaFunc() { try { let sum = 0; for (let val of [1, 2, 3]) { sum += val; } throw new Error(`sync error, sum = ${sum}`); } catch(err) { return err; } } } let test = new TestClass(); checkOptimizationStatus(test.megaFunc); 


 Function is optimized by TurboFan 


What remains to be not optimized, as well as checking your function for optimization or deoptimization, literally in 1 action can be seen under the cut

')

2. Unsupported syntax



Currently not optimized:



  • function generators;
  • functions containing a for-of expression;
  • functions containing a try-catch expression;
  • functions containing a try-finally statement;
  • functions containing a compound let assignment statement;
  • functions containing the const compound assignment operator;
  • functions that contain object literals, which in turn contain __proto__, get, or set declarations.


Most likely, not optimized:



  • functions containing a debugger expression;
  • functions that call eval ();
  • functions containing a with expression.


A year ago, this list seemed impressive, at the moment only debugger, generators and "__proto__, get or set" are not optimized from the list, even try-catch no longer requires a tryCatch trick.



3. Using arguments



There are many ways to use arguments so that it will not be possible to optimize a function. So when working with arguments you should be especially careful.

...


With arguments, everything is simple, most likely they will not be optimized, and it is already quite easy to switch to rest parameters with which you can work as you please.



4. Switch-case



The switch-case expression today can have up to 128 case points, and if this number is exceeded, the function containing this expression cannot be optimized.


Now even 500 cases do not cause de-optimization, 600 too.



5. For-in



A For-in expression can interfere with function optimization in several ways. 5.1. The key is not a local variable.

5.2. The object being iterated is not “simple enumerable”.

5.2.2. There are fields with enumerated values ​​in the object prototype chain.

5.2.3. The object contains enumerated array indices.



Everything is optimized, except for the situation when the key for for-in is defined from the outside of the function (but still no one will write):



 var key; function nonLocalKey2() { var obj = {} for(key in obj); } 


6. Infinite loops with complex logic of exit conditions or with unclear exit conditions.



It was not possible to select such an infinite loop so that deoptimization would result.



How to check your functions for optimization yourself



Make it easy enough for chrome and for the node. In both cases, you just need to run them with the --allow-natives-syntax flag.



For chrome, create a shortcut:



 "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --js-flags="--allow-natives-syntax" 


Files index.html



 <script src="index.js"></script> 


and index.js



 function exampleFunction() { return 3; eval(''); } checkOptimizationStatus(exampleFunction) function checkOptimizationStatus(exampleFunction) { exampleFunction(); exampleFunction(); %OptimizeFunctionOnNextCall(exampleFunction); exampleFunction(); switch (%GetOptimizationStatus(exampleFunction)) { case 1: console.log("Function is optimized"); break; case 2: console.log("Function is not optimized"); break; case 3: console.log("Function is always optimized"); break; case 4: console.log("Function is never optimized"); break; case 6: console.log("Function is maybe deoptimized"); break; case 7: console.log("Function is optimized by TurboFan " + exampleFunction.name); break; case 49: console.log("Function is optimized by NewMethod " + exampleFunction.name); break; default: console.log("Unknown optimization status"); break; } } 


And just open index.html in the browser. There is no need for a web server, just a regular html page.



For node is even easier:



 node --allow-natives-syntax index.js 


The wrapper function checkOptimizationStatus (yourFunction) will show the optimization status, just call it by passing your function as a parameter



Total



From the impressive list of killers, there are literally 2-3 uncritical cases that not everyone uses.

New optimizations appear fairly quickly, so you can just throw this list out of your head and calmly write to js in a familiar / convenient style.



PS: In chrome 55, there was support for async-await without a flag, in the node starting with 8 branches, promis functions are successfully optimized, so it’s not long to wait when async-await will also be optimized.



UPD: Optimization for async-await added to chrome canary 57

Results for async-await functions
 async function delayAsync(delay) { return new Promise(resolve => { setTimeout(() => resolve(), delay) }) } async function asyncTest() { return 'habrahabr' } async function exampleFunction() { try { let result = await asyncTest() await delayAsync(500) console.log(`result after 500ms: ${result}`) } catch (err) { console.error(err) } } 


 Function is optimized by TurboFan: exampleFunction Function is optimized by TurboFan: asyncTest Function is optimized by TurboFan: delayAsync // 500ms,    (3) after 500ms: habrahabr 


Source: https://habr.com/ru/post/319936/



All Articles