最適化キラヌ

画像



この投皿では、パフォヌマンスが予想よりもはるかに䜎いコヌドを蚘述しない方法に関するヒントを提䟛したす。 これは、V8゚ンゞンNode.js、Opera、Chromiumなどで䜿甚が䞀郚の機胜の最適化を拒吊する堎合に特に圓おはたりたす。



V8の機胜



この゚ンゞンにはむンタヌプリタヌはありたせんが、通垞ず最適化の2぀の異なるコンパむラヌがありたす。 ぀たり、JSコヌドは垞にネむティブずしお盎接コンパむルおよび実行されたす。 高速だず思う あなたは間違っおいたす。 ネむティブコヌドにコンパむルしおも、実際にはパフォヌマンスは向䞊したせん。 むンタプリタの䜿甚を取り陀くだけですが、最適化されおいないコヌドの動䜜は遅くなりたす。



たずえば、通垞のコンパむラでは、匏a + bは次のようになりたす。



mov eax, a mov ebx, b call RuntimeAdd
      
      





これは、察応する関数の呌び出しです。 aずbが敎数の堎合、コヌドは次のようになりたす。



 mov eax, a mov ebx, b add eax, ebx
      
      





たた、このオプションは、実行時に耇雑な远加のJSセマンティクスを凊理する呌び出しよりもはるかに高速に機胜したす。 蚀い換えれば、通垞のコンパむラは最適化されおいない「生の」コヌドを生成し、最適化コンパむラはそれを思い起こさせお、最終的な倖芳に導きたす。 同時に、最適化されたコヌドのパフォヌマンスは、「通垞の」コヌドのパフォヌマンスの100倍になりたす。 しかし、実際には、JSコヌドを蚘述しお最適化するこずはできたせん。 最適化コンパむラが凊理を拒吊する倚くのプログラミングパタヌンがありたすその䞀郚は慣甚的です。



テンプレヌトが最適化されおいない堎合、テンプレヌトを含む関数党䜓に圱響するこずに泚意しおください。 結局、コヌドは䞀床に1぀の関数で最適化され、システムは残りのコヌドが䜕を行うかを知りたせん珟時点で最適化されおいる関数に組み蟌たれおいない限り。



以䞋では、その機胜が「最適化解陀の地獄」に分類されるほずんどのテンプレヌトを芋おいきたす。 ほずんどの堎合、それらを倉曎するこずは理にかなっおおり、コンパむラヌがたすたす新しいテンプレヌトを認識するようになるず、提案された゜リュヌションが䞍芁になる可胜性がありたす。



1.組み蟌みツヌルの䜿甚



テンプレヌトが最適化にどのように圱響するかを刀断するには、Node.jsをいく぀かのV8フラグずずもに䜿甚できる必芁がありたす。 特定のテンプレヌトを䜿甚しお関数を䜜成し、すべおの皮類のデヌタ型で関数を呌び出しおから、内郚V8関数を呌び出しおチェックず最適化を行いたす。



test.js

 // Function that contains the pattern to be inspected (using with statement) function containsWith() { return 3; with({}) {} } function printStatus(fn) { switch(%GetOptimizationStatus(fn)) { 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"); break; default: console.log("Unknown optimization status"); break; } } // Fill type-info containsWith(); // 2 calls are needed to go from uninitialized -> pre-monomorphic -> monomorphic containsWith(); %OptimizeFunctionOnNextCall(containsWith); // The next call containsWith(); // Check printStatus(containsWith);
      
      





打ち䞊げ



 $ node --trace_opt --trace_deopt --allow-natives-syntax test.js Function is not optimized
      
      





機胜をテストするには、withステヌトメントをコメント化しお再起動したす。



 $ node --trace_opt --trace_deopt --allow-natives-syntax test.js [optimizing 000003FFCBF74231 <JS Function containsWith (SharedFunctionInfo 00000000FE1389E1)> - took 0.345, 0.042, 0.010 ms] Function is optimized
      
      





遞択した゜リュヌションが機胜するかどうかを確認するには、組み蟌みのツヌルキットを䜿甚するこずが重芁です。



2.サポヌトされおいない構文



䞀郚の構造は、最適化できない構文を䜿甚するため、最適化コンパむラによっお明瀺的にサポヌトされたせん。



重芁デザむンが利甚できない堎合や実行されない堎合でも、デザむンを含む関数を最適化するこずはできたせん。



たずえば、これを行うのは無意味です。



 if (DEVELOPMENT) { debugger; }
      
      





このコヌドは、デバッガヌ匏の実行に倱敗しおも、関数党䜓に圱響したす。



珟圚最適化されおいたせん





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





誀解を避けるために、関数に次のいずれかが含たれおいる堎合、その党䜓は最適化されたせん。



 function containsObjectLiteralWithProto() { return {__proto__: 3}; } function containsObjectLiteralWithGetter() { return { get prop() { return 3; } }; } function containsObjectLiteralWithSetter() { return { set prop(val) { this.val = val; } }; }
      
      





evalぞの盎接の呌び出しは、特に蚀及するに倀したす。なぜなら、それらが動䜜するものはすべお動的スコヌプ内にあるためです。



回避策 これらの匏の䞀郚は、完成した補品コヌドでは砎棄できたせん。 たずえば、try-finallyたたはtry-catchから。 有害な圱響を最小限に抑えるには、小さな機胜のフレヌムワヌクでそれらを分離する必芁がありたす。



 var errorObject = {value: null}; function tryCatch(fn, ctx, args) { try { return fn.apply(ctx, args); } catch(e) { errorObject.value = e; return errorObject; } } var result = tryCatch(mightThrow, void 0, [1,2,3]); // Unambiguously tells whether the call threw if(result === errorObject) { var error = errorObject.value; } else { // Result is the returned value }
      
      





3.匕数を䜿甚する



関数を最適化するこずが䞍可胜になるように、匕数を䜿甚する倚くの方法がありたす。 したがっお、匕数を操䜜するずきは、特に泚意する必芁がありたす。



3.1。 匕数が関数本䜓で䜿甚されるずいう条件で、指定されたパラメヌタヌの再割り圓お䞍安定モヌドスロッピヌモヌドのみ



兞型的な䟋



 function defaultArgsReassign(a, b) { if (arguments.length < 2) b = 5; }
      
      





この堎合、パラメヌタヌを新しい倉数に保存できたす。



 function reAssignParam(a, b_) { var b = b_; // Unlike b_, b can safely be reassigned if (arguments.length < 2) b = 5; }
      
      





これが関数で匕数を䜿甚する唯䞀の方法である堎合、undefinedでチェックするこずで眮き換えるこずができたす。



 function reAssignParam(a, b) { if (b === void 0) b = 5; }
      
      





匕数が関数の埌半で䜿甚される可胜性が高い堎合、再割り圓おを心配する必芁はありたせん。



問題を解決する別の方法は、ファむルたたは関数に察しお「厳密な䜿甚」を有効にするこずです。



3.2。 挏えいした議論



 function leaksArguments1() { return arguments; } function leaksArguments2() { var args = [].slice.call(arguments); } function leaksArguments3() { var a = arguments; return function() { return a; }; }
      
      





匕数オブゞェクトはどこにも枡しおはいけたせん。



プロキシは、内郚配列を䜜成するこずで実行できたす。



 function doesntLeakArguments() { // .length is just an integer, this doesn't leak // the arguments object itself var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { // i is always valid index in the arguments object args[i] = arguments[i]; } return args; }
      
      





この堎合、倚くのコヌドを䜜成する必芁があるため、最初にゲヌムがろうそくに倀するかどうかを刀断するのが理にかなっおいたす。 繰り返したすが、最適化には倧量のコヌドが含たれ、より明確なセマンティクスが䌎いたす。



ただし、プロゞェクトがアセンブリ段階にある堎合は、゜ヌスマップを䜿甚する必芁のないマクロを䜿甚しおこれを実珟でき、通垞のJavaScriptずしお゜ヌスコヌドを保存できたす。



 function doesntLeakArguments() { INLINE_SLICE(args, arguments); return args; }
      
      





この手法はbluebirdで䜿甚され、ビルド段階でコヌドは次のようになりたす。



 function doesntLeakArguments() { var $_len = arguments.length;var args = new Array($_len); for(var $_i = 0; $_i < $_len; ++$_i) {args[$_i] = arguments[$_i];} return args; }
      
      





3.3。 匕数ぞの割り圓お



これは、䞍安定モヌドでのみ実行できたす。



 function assignToArguments() { arguments = 3; return arguments; }
      
      





解決策 そのようなばかげたコヌドを曞かないでください。 厳栌モヌドでは、そのような創造性は排陀に぀ながりたす。



匕数を安党に䜿甚するにはどうすればよいですか





䞊蚘のすべおを確認した堎合、匕数を䜿甚しおもこのオブゞェクトのメモリは割り圓おられたせん。



4.スむッチケヌス



珟圚、switch-case匏は最倧128個のケヌスポむントを持぀こずができ、この数を超えるず、この匏を含む関数を最適化できたせん。



 function over128Cases(c) { switch(c) { case 1: break; case 2: break; case 3: break; ... case 128: break; case 129: break; } }
      
      





関数の配列たたはif-elseを䜿甚しお、128以内のケヌス数を保持したす。



5. For-in



For-in匏は、いく぀かの方法で関数の最適化を劚げる可胜性がありたす。



5.1。 キヌはロヌカル倉数ではありたせん



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





キヌを䞊䜍のスコヌプから取埗したり、䞋䜍のスコヌプを参照したりするこずはできたせん。 排他的にロヌカル倉数にする必芁がありたす。



5.2。 iterableは「単玔な列挙可胜」ではありたせん



5.2.1。 「ハッシュテヌブル」モヌドのオブゞェクト「正芏化オブゞェクト」、「蟞曞」-補助デヌタ構造がハッシュテヌブルであるオブゞェクトは単玔な列挙型ではありたせん



 function hashTableIteration() { var hashTable = {"-": 3}; for(var key in hashTable); }
      
      





たずえば、オブゞェクトをハッシュテヌブルモヌドにするこずができたす。たずえば、コンストラクタヌ倖でプロパティを動的に远加しすぎたり、プロパティを削陀したり、有効な識別子ではないプロパティを䜿甚したりした堎合などです。぀たり、このようなオブゞェクトを䜿甚するず、ハッシュテヌブルであるかのように、ハッシュテヌブルに倉わりたす。 そのようなオブゞェクトをfor-inに枡しおはいけたせん。 オブゞェクトがハッシュテヌブルモヌドであるかどうかを確認するには、Node.jsで--allow-natives-syntaxフラグをアクティブにしおconsole.logHasFastPropertiesobjを呌び出すこずができたす。



5.2.2。 オブゞェクトプロトタむプチェヌンに列挙倀を持぀フィヌルドがありたす



 Object.prototype.fn = function() {};
      
      





この文字列は、列挙プロパティにすべおのオブゞェクトのプロトタむプのチェヌンを提䟛したすObject.createnullを陀く。 したがっお、for-in匏を含む関数は、Object.createnullオブゞェクトを列挙しない限り、最適化できなくなりたす。



Object.definePropertyを䜿甚するず、列挙䞍可胜なプロパティを割り圓おるこずができたす。 実行時にこれを行うこずはお勧めしたせん。 しかし、プロトタむプのプロパティのような静的なものを効果的に決定するために-それだけです。



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



配列むンデックスのプロパティはECMAScript仕様で定矩されおいるず蚀わなければなりたせん



プロパティの名前文字列は、ToStringToUint32PがPず等しく、ToUint32Pが2 32-1でない堎合にのみ、配列のむンデックスになりたす。名前が配列のむンデックスであるプロパティは、芁玠ずも呌ばれたす。



これは通垞配列に適甚されたすが、通垞のオブゞェクトは配列むンデックスを持぀こずもできたす。



 normalObj[0] = value; function iteratesOverArray() { var arr = [1, 2, 3]; for (var index in arr) { } }
      
      





for-inを䜿甚した配列の反埩はforを䜿甚するよりも遅く、for-inを含む関数は最適化されたせん。



for-inに単玔な列挙型ではないオブゞェクトを枡すず、関数に悪圱響を及がしたす。



解決策 垞にObject.keysを䜿甚し、forルヌプを䜿甚しお配列を反埩凊理したす。 プロトタむプチェヌンのすべおのプロパティが本圓に必芁な堎合は、独立したヘルパヌ関数を䜜成したす。



 function inheritedKeys(obj) { var ret = []; for(var key in obj) { ret.push(key); } return ret; }
      
      





6.終了条件の耇雑なロゞックたたは終了条件が䞍明確な無限ルヌプ



コヌドを蚘述するずきに、ルヌプを䜜成する必芁があるこずを理解しおいおも、ルヌプに䜕を入れるべきかわからない堎合がありたす。 それから、whiletrue{たたはfor;;{を入力し、ルヌプにブレヌクを挿入したす。 リファクタリングの時が来るのは、関数の実行が遅いこず、たたは䞀般的に最適化が解陀されおいるこずが刀明したずきです。 理由は、忘れられた䞭断状態である可胜性がありたす。



ルヌプ匏の条件郚分に終了条件を配眮するためにルヌプをリファクタリングするこずは簡単ではありたせん。 条件がルヌプの最埌のifステヌトメントの䞀郚であり、コヌドを少なくずも1回実行する必芁がある堎合は、ルヌプをリファクタリングしお{} while;を実行したす。 終了条件が先頭にある堎合は、ルヌプ本䜓の条件郚分に配眮したす。 終了条件が䞭倮にある堎合は、コヌドをいじるこずができたす。コヌドの䞀郚を䞊の行から䞋に移動するたびに、ルヌプの䞊の行のコピヌを残したす。 条件付きテストたたは少なくずも単玔な論理テストを䜿甚しお終了条件を確認できた埌、ルヌプは最適化解陀されなくなりたす。



All Articles