JS最適化キラーはもう怖くない

1年前、 Optimization Killerの翻訳を見ましたが、 最適化されたjsコードを書くためにどれだけ心に留めておく必要があるかに驚いていました。 ほとんどすべてのes6が最適化されていないことは、特にイライラしていました。









そして今、TurboFanと呼ばれるv8の新しいオプティマイザーは、この1年でes6、es5のほぼすべてを最適化することを学び、try-catchでさえ問題ではなくなりました。



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
      
      





最適化されていないもの、および最適化または最適化解除のために関数をチェックする方法は、文字通り1アクションでカットの下で見ることができます



2.サポートされていない構文



現在最適化されていません:



  • ジェネレーター関数;
  • for-of式を含む関数。
  • try-catch式を含む関数。
  • try-finally式を含む関数。
  • 複合代入演算子letを含む関数。
  • const代入演算子constを含む関数。
  • オブジェクトリテラルを含む関数には、__ proto __、get、またはset宣言が含まれます。


おそらく、最適化できない:



  • デバッガー式を含む関数。
  • eval()を呼び出す関数;
  • 式を含む関数。


1年前、このリストは印象的でした。現時点では、デバッガー、ジェネレーター、および「__proto __、getまたはset」のみがリストから最適化されておらず、try-catchでもtryCatchトリックは不要です。



3.引数を使用する



関数を最適化することが不可能になるように、引数を使用する多くの方法があります。 したがって、引数を操作するときは、特に注意する必要があります。

...


引数を使用するとすべてが単純になり、最適化されない可能性が高くなります。また、必要に応じて操作できる残りのパラメーターに切り替えることは既に非常に簡単です。



4.スイッチケース



現在、switch-case式は最大128個のケースポイントを持つことができ、この数を超えると、この式を含む関数を最適化できません。


現在、500ケースでも最適化が解除されず、600ケースでも最適化が解除されません。



5. For-in



For-in式は、いくつかの方法で関数の最適化を妨げる可能性があります。 5.1。 キーはローカル変数ではありません

5.2。 iterableは「単純な列挙可能」ではありません

5.2.2。 オブジェクトプロトタイプチェーンに列挙値を持つフィールドがあります

5.2.3。 オブジェクトには列挙された配列インデックスが含まれます。



for-inのキーが関数の外部から定義されている場合を除き、すべてが最適化されています(ただし、だれもそれを記述しません)。



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





6.終了条件の複雑なロジックまたは終了条件が不明確な無限ループ



最適化を解除するためにこのような無限ループを拾うことはできませんでした。



最適化のための関数を自分でテストする方法



これを行うには、クロムとノードにとって十分に簡単です。 どちらの場合も、フラグ--allow-natives-syntaxを指定して実行するだけです



クロムのショートカットを作成します。



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





Index.htmlファイル



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





および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; } }
      
      





そして、ブラウザでindex.htmlを開いてください。 Webサーバーは不要で、通常のHTMLページが必要です。



ノードの場合はさらに簡単です:



 node --allow-natives-syntax index.js
      
      





ラッパー関数checkOptimizationStatus(yourFunction)は最適化ステータスを表示します。関数をパラメーターとして渡すことで最適化ステータスを呼び出します。



まとめ



キラーの印象的なリストから、文字通り2〜3の重要でないケースがありますが、誰もが使用するわけではありません。

新しい最適化はすぐに表示されるため、このリストを頭から外して、使い慣れた便利なスタイルでjsを落ち着いて書くことができます。



PS: Chrome 55では、フラグなしのasync-awaitのサポートが登場し、ブランチ8から始まるノードでは、promise関数が正常に最適化されるため、async-awaitも最適化されるのに時間がかかりません。



UPD:Chrome Canary 57にasync-awaitの最適化を追加

非同期待機関数の結果
 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
      
      








All Articles