TurboFan V8の仕様はコードの最適化にどのように影響しますか? 近い将来に自分自身を示すために、今日の技術はどのように最適と考えられていますか? 最近のV8パフォーマンスキラーはどのように動作しますか?また、それらに何を期待できますか? この資料では、これらおよび他の多くの質問に対する答えを見つけようとしました。
デビッド・マーク・クレメンツとマッテオ・コリーナの共同作業の成果がここにあります。 素材は、V8開発チームのFrancis HinkelmannとBenedict Meirerによって確認されました。
![](https://habrastorage.org/web/d0d/665/0d6/d0d6650d6750404c8b28470502273a49.jpg)
JavaScriptを高速で実行できるV8エンジンの中心部分は、JIT(Just In Time)コンパイラーです。 実行時にコードを最適化できる動的コンパイラーです。 V8が最初に作成されたとき、JITコンパイラーはFullCodeGenと名付けられました。これは( Yang Guoが正しく述べているように)このプラットフォームの最初の最適化コンパイラーです。 次に、V8チームはCrankshaftコンパイラを作成しました。これには、FullCodeGenには実装されていない多くのパフォーマンス最適化が含まれていました。
90年代からJavaScriptを見て、ずっと使っていた人として、どのエンジンに関係なく、JSコードのどのセクションが動作が遅く、すぐに完全に非自明になることがしばしばあることに気付きました。使用されます。 プログラムが予想よりも遅く実行された理由は、しばしば理解するのが困難でした。
近年、Matteo Collinaと私は、Node.jsの高性能コードを記述する方法を見つけることに焦点を当ててきました。 当然、これは、V8 JSエンジンによってコードが実行されるときに、どのアプローチが高速で、どのアプローチが遅いかを知ることを意味します。
V8チームが新しいJITコンパイラーTurboFanを作成したので、今度はパフォーマンスに関するすべての前提条件を確認します。
最適化コンパイルの放棄につながる、よく知られたソフトウェア構成を検討します。 さらに、ここでは、さまざまなバージョンのV8のパフォーマンスを研究することを目的とした、より複雑な研究を扱います。 これはすべて、異なるバージョンのNodeおよびV8を使用して起動される一連のマイクロベンチマークを通じて行われます。
もちろん、V8用にコードを最適化する前に、最初にAPI、アルゴリズム、およびデータ構造の設計に焦点を合わせる必要があります。 これらのマイクロベンチマークは、NodeでのJavaScriptのパフォーマンスがどのように変化しているかを示す指標と見なすことができます。 これらのインジケーターを使用して、コードの全体的なスタイルと、通常の最適化を適用した後にパフォーマンスを改善する方法を変更できます。
バージョンV8 5.1、5.8、5.9、6.0、および6.1のマイクロベンチマークのパフォーマンスを検討します。
V8バージョンとNodeバージョンの関係を明確にするために、次の点に注意してください。V85.1エンジンはNode 6で使用され、Crankshaft JITコンパイラはここで使用され、V8 5.8エンジンはNodeバージョン8.0〜8.2で使用され、Crankshaftはここで使用されます。ターボファン。
現在、ノード8.3、またはおそらく8.4には、V8エンジンバージョン5.9または6.0が存在することが予想されます。 この記事の執筆時点でのV8の最新バージョンは6.1です。 node-v8実験リポジトリのNodeに統合されています。 言い換えれば、V8 6.1はNodeの将来のバージョンで使用されることになります。
この記事の準備で使用されたテストコードとその他の資料は、 ここにあります。
これは、特に未処理のテスト結果があるドキュメントです。
ほとんどのマイクロベンチマークは、Macbook Pro 2016、3.3 GHz Intel Core i7、16 GB 2133 MHz LPDDR3メモリで実行されます。 それらの一部(数値の操作、オブジェクトのプロパティの削除)は、MacBook Pro 2014、3 GHz Intel Core i7、16 GB 1600 MHz DDR3メモリで実行されました。 Node.jsの異なるバージョンのパフォーマンス測定は、同じコンピューターで実行されました。 他のプログラムがテスト結果に影響しないことを確認しました。
テストを見て、結果が将来のノードにとって何を意味するかについて話しましょう。 すべてのテストは、 benchmark.jsパッケージを使用して実行されました。各図のデータは、1秒あたりの操作数を示しています。つまり、値が大きいほど優れています。
問題を試す/キャッチする
よく知られている最適化解除パターンの1つは、
try/catch
ブロックを使用することです。
以下、テストの説明のリストの括弧内に、英語の短いテスト名が記載されていることに注意してください。 これらの名前は、チャートで結果を示すために使用されます。 さらに、テスト中に使用されたコードをナビゲートするのに役立ちます。
このテストでは、4つのテストケースを比較します。
- その中にある
try/catch
計算を実行する関数(sum with tr catch)。
-
try/catch
ブロックなしで計算を実行する関数(try catchなしの合計)。
-
try
(sum wrapped)ブロック内で計算を実行する関数呼び出し。
-
try/catch
を使用せずに計算を実行する関数呼び出し(合計関数)。
→ GitHubでコードをテストする
![](https://habrastorage.org/getpro/habr/post_images/726/449/b82/726449b825635e940f6c18552ac618e4.png)
try/catch
パフォーマンスへの悪影響について既に知られていることがNode 6(V8 5.1)で確認されており、Node 8.0-8.2(V8 5.8)
try/catch
パフォーマンスへの影響ははるかに小さいことがわかります。
また、
try
ブロックから関数を呼び出すことは、
try
外部で呼び出すよりもはるかに遅いことに注意する必要があります。これは、ノード6(V8 5.1)およびノード8.0-8.2(V8 5.8)の両方に当てはまります。
ただし、ノード8.3+では、
try
ブロックから関数を呼び出してもパフォーマンスにはほとんど影響しません。
それにもかかわらず、落ち着かないでください。 最適化セミナーのいくつかの資料に取り組んでいる間に、かなり特定の状況がTurboFanでの最適化解除/再最適化の無限のサイクルにつながる可能性がある場合にエラーを発見しました。 これは、次のパフォーマンスキラーパターンと考えられます。
オブジェクトからプロパティを削除する
長年にわたり、JSで高性能コードを記述したい人はだれでも(少なくとも、プログラムの最も負荷の高い部分に最適なコードを記述する必要がある場合)、
delete
コマンドは回避されていました。
delete
の問題は、V8がJavaScriptオブジェクトの動的な性質を処理する方法と、低レベルのエンジン実装でのプロパティの検索を複雑にするプロトタイプチェーン(潜在的に動的)に起因します。
プロパティを持つ高性能オブジェクトを作成するV8エンジンのアプローチは、オブジェクトの「フォーム」、つまりオブジェクトが持つキーと値(プロトタイプチェーンのキーと値を含む)に基づいて、C ++レベルでクラスを作成することです。 これらの構造は、「隠しクラス」として知られています。 ただし、このタイプの最適化はプログラムの実行中に実行されます。 オブジェクトの形状が不明な場合、V8には別のプロパティ検索モードがあります:ハッシュテーブル検索。 このようなプロパティ検索は非常に遅くなります。
従来、
delete
コマンドを使用してオブジェクトからキーを削除する場合、プロパティにアクセスする後続の操作は、ハッシュテーブルを検索して実行されます。 そのため、プログラマーは
delete
コマンドを使用せず、代わりにプロパティを
undefined
に設定します。値を破壊するという点では、同じ結果になりますが、プロパティの存在を確認する際に複雑さが増します。 ただし、通常、このアプローチは、たとえば
JSON.stringify
出力に
undefined
値が含まれ
undefined
ため(JSON仕様によると
undefined
は有効な値に適用されないため)、シリアル化のためにオブジェクトを準備するときに十分です。
ここで、新しいTurboFan実装がオブジェクトからプロパティを削除する問題を解決するかどうかを調べましょう。
ここでは、3つのテストケースを比較します。
- プロパティが
undefined
設定された後のオブジェクトのundefined
(undefined
設定)。
-
delete
(delete)コマンドを使用してプロパティを削除した後のオブジェクトのシリアル化。
-
delete
コマンドが最後に追加されたプロパティを削除するために使用された後にオブジェクトをシリアル化する(最後のプロパティを削除する)。
→ GitHubでコードをテストする
![](https://habrastorage.org/getpro/habr/post_images/a26/d93/65c/a26d9365cb1bf6d82ce2085a3d556f72.png)
V8 6.0および6.1(ノードリリースではまだ使用されていません)では、オブジェクトに最後に追加されたプロパティを削除すると、プログラム実行の最適化されたTurboFanパスに対応するため、プロパティを
undefined
設定するよりも高速です。 これは、V8開発チームが
delete
コマンドのパフォーマンスの改善に取り組んでいることを示しているため、非常に優れています。
ただし、この演算子を使用すると、追加されたプロパティの最後ではないプロパティがオブジェクトから削除された場合、プロパティにアクセスするときに深刻なパフォーマンスの低下につながります。 この観察はJacob Kummerovによって助けられました。JacobKummerovは、最後に追加されたプロパティを削除するオプションのみが調査されたテストの特異性を指摘しました。 彼に感謝します。 その結果、
delete
コマンドは将来のNodeリリース用に記述されたコードでどれだけ使用でき、また使用すべきだと言っても、これを行わないことをお勧めします。
delete
コマンドは引き続きパフォーマンスに悪影響を及ぼします。
リークと引数の配列への変換
通常の関数で使用できる暗黙的に生成された
arguments
オブジェクトの典型的な問題(
arguments
オブジェクトの矢印関数は使用できません)は、配列ではなく配列のように見えることです。
配列のメソッドまたはその動作の機能を使用するには、
arguments
のインデックス付きプロパティを配列にコピーする必要があります。 過去に、JS開発者はより短いコードとより速いコードを同一視する傾向がありました。 このアプローチは、クライアントコードの場合、ブラウザーがダウンロードするデータの量を削減することを可能にしますが、サーバーコードで問題を引き起こす可能性があります。サーバーコードでは、プログラムのサイズが実行速度よりもはるかに重要ではありません。 その結果、
arguments
オブジェクトを配列に変換する非常に短い方法が非常に一般的になりました:
Array.prototype.slice.call(arguments)
。 このようなコマンドは、
Array
オブジェクトの
slice
メソッドを呼び出し、
arguments
オブジェクトをこのメソッドの
this
コンテキストとして渡します。
slice
メソッドは、配列のように見えるオブジェクトを検出し、その後にジョブを実行します。 その結果、
arguments
オブジェクトのコンテンツから配列に組み立てられた配列を取得します。
ただし、暗黙的に生成された
arguments
オブジェクトが関数のコンテキスト外に渡される場合(たとえば、
Array.prototype.slice.call(arguments)
呼び出すときなど、関数から返されるか別の関数に渡される場合)、これは通常パフォーマンス
Array.prototype.slice.call(arguments)
引き起こします。 この声明を調べます。
次のマイクロベンチマークは、V8の4つのバージョンで相互に関連する2つの状況を調査することを目的としています。 つまり、これは
arguments
リークのコストと配列に
arguments
をコピーするコストであり、
arguments
オブジェクトの代わりに関数の外部に渡されます。
テストケースは次のとおりです。
-
arguments
を配列に変換せずに、arguments
オブジェクトを別の関数に渡します(漏れやすい引数)。
-
Array.prototype.slice
(Array.prototype.slice arguments)コンストラクトを使用して、arguments
オブジェクトのコピーを作成します。
-
for
ループを使用しfor
各プロパティをコピーします(forループコピー引数)
- EcmaScript 2015の拡張演算子を使用して、入力データの配列をスプレッド演算子として関数に割り当てます。
→ GitHubでコードをテストする
![](https://habrastorage.org/getpro/habr/post_images/307/038/32c/30703832c8c11482e20b456f513358b9.png)
次に、パフォーマンス特性の変化を強調するために、折れ線グラフの形式で表示される同じデータを見てみましょう。
![](https://habrastorage.org/getpro/habr/post_images/586/272/b30/586272b3015f1ac04aa67be2589eb205.png)
これらすべてから導き出せる結論を以下に示します。 関数の入力データを配列の形で処理する生産的なコードを書く必要がある場合(経験から私は非常に頻繁に必要とすることを知っています)、ノード8.3以降では拡張演算子を使用する必要があります。 Node 8.2以下では、
for
ループを使用して、
arguments
からキーを新しい(以前に作成された)配列にコピーする必要があります(詳細については、テストコードを参照)。
さらに、ノード8.3+では、
arguments
オブジェクトを他の関数に渡すとパフォーマンスが低下するため、完全な配列を必要とせず、配列のように見えるが配列ではない構造を操作できる場合、他のパフォーマンス上の利点があります。
部分使用(カリー化)および関数コンテキストバインディング
関数を部分的に適用(またはカリー化)すると、囲まれた回路の可視領域に特定の状態を保存できます。
例:
function add (a, b) { return a + b } const add10 = function (n) { return add(10, n) } console.log(add10(20))
この例では、
add
関数のパラメーターは、
add10
関数の数値10として部分的に適用されます。
bind
メソッドのおかげで、EcmaScript 5以降、関数のより短い形式の部分的な使用が可能になりました。
function add (a, b) { return a + b } const add10 = add.bind(null, 10) console.log(add10(20))
ただし、通常、
bind
メソッドは上記のクロージャーメソッドよりも大幅に遅いため、使用されません。
このテストでは、V8の異なるバージョンで
bind
とスナップの使用の違いを測定します。 比較のために、ここでは元の関数の直接呼び出しが使用されています。
以下に4つのテストケースを示します。
- 最初の引数(通貨)の予備的な部分適用で別の関数を呼び出す関数。
- 最初の引数が部分的に適用された別の関数を呼び出す矢印関数(太い矢印カレー)。
-
bind
メソッドを使用して作成された関数。最初の引数を別の関数に部分的に適用します(バインド)。
- 部分的なアプリケーションを使用せずに関数を直接呼び出します(直接呼び出し)。
→ GitHubでコードをテストする
![](https://habrastorage.org/getpro/habr/post_images/0b3/e84/e62/0b3e84e628c8b8701af491b466562560.png)
テスト結果の線形図は、最新バージョンのV8で機能を操作するための考慮された方法の間にほとんど完全な違いがないことを明確に示しています。 興味深いことに、矢印関数を使用する部分的なアプリケーションは、通常の関数を使用するよりもはるかに高速です(少なくともテストでは)。 実際、直接関数呼び出しとほぼ一致します。 V8 5.1(ノード6)および5.8(ノード8.0-8.2)では、
bind
非常に遅く、これらの目的で矢印関数を使用すると最高速度を達成できることが明らかです。 ただし、V8バージョン5.9(ノード8.3+)以降、
bind
パフォーマンスは大幅に向上しています。 このアプローチは、V8 6.1(将来のバージョンのノード)で最速です(ただし、ここでのパフォーマンスの違いはほとんど区別できません)。
Nodeのすべてのバージョンで最速のカレー方法は、矢印関数を使用することです。 最近のバージョンでは、この方法と
bind
の使用の違い
bind
重要で
bind
ません。現在の状態では、通常の関数を使用するよりも高速です。 ただし、より完全な全体像を得るには、さまざまなサイズのデータ構造を持つ関数のより多くのタイプの部分適用を調査する必要があるため、得られた結果がどのような状況でも有効であるとは言えません。
機能コードサイズ
署名、スペース、さらにはコメントを含む関数のサイズは、V8が関数をインライン化できるかどうかに影響を与える可能性があります。 はい。関数にコメントを追加すると、パフォーマンスが約10%低下する可能性があります。 これは将来変更されますか?
このテストでは、3つのシナリオを検討します。
- 小さな関数を呼び出します(小さな関数の合計)。
- コメントで補完され、組み込みモードで実行される小さな機能の作業(すべて一緒に)。
- コメント付きの大きな関数の呼び出し(長い関数の合計)。
→ GitHubでコードをテストする
![](https://habrastorage.org/getpro/habr/post_images/3d5/8dd/d99/3d58ddd99669b43d4ba300dcc6be4eb5.png)
V8 5.1(ノード6)では、small関数とlongの合計テストの合計は同じ結果を示します。 これは、埋め込みの仕組みを完全に示しています。 小さな関数を呼び出すと、V8がこの関数の内容を呼び出される場所に書き込むことに似ています。 したがって、関数のテキストを記述するとき(コメントを追加しても)、呼び出しの場所に手動で埋め込みますが、パフォーマンスは同じです。 繰り返しになりますが、V8 5.1(ノード6)では、関数が特定のサイズに達した後、コメントが追加された関数を呼び出すと、コードの実行が大幅に遅くなることがわかります。
Node 8.0-8.2(V8 5.8)では、小さな関数を呼び出すコストが著しく増加したことを除いて、全体として状況は同じままです。 これはおそらく、CrankshaftとTurboFanの要素の混合によるものです。1つの関数がCrankshaftにあり、もう1つの関数がTurboFanにある場合、埋め込みのメカニズムの故障につながります(つまり、連続して組み込まれた関数のクラスター間の移行が発生するはずです)。
V8 5.9以降(ノード8.3以降)では、スペースやコメントなどの無関係な文字を追加しても、関数のパフォーマンスには影響しません。 これは、Curboshaftなどの文字をカウントする代わりに、TurboFanが抽象構文ツリー (AST、 抽象構文ツリー )を使用して関数のサイズを計算するためです。 TurboFanは、関数のバイト数を考慮する代わりに、関数の実際の命令を分析します。そのため、V8 5.9(ノード8.3+) スペースから始まり、変数名を構成する文字、関数シグネチャ、およびコメントは、関数が埋め込まれる 。 さらに、関数の全体的なパフォーマンスが低下していることに注意する必要があります。
ここでの主な結論は、機能はできる限り小さくする価値があるということです。 現時点では、関数内の不要なコメント(さらにはスペース)を避ける必要があります。 さらに、最高のパフォーマンスを目指している場合、関数を手動で埋め込む(つまり、関数コードを呼び出しの場所に転送し、関数を呼び出す必要がなくなる)ことは、最速のアプローチであり続けます。 もちろん、ここでバランスを取る必要があります。実際の実行可能コードが特定のサイズに達すると、関数はとにかく組み込まれないため、他の関数のコードを思いがけずコピーすると、パフォーマンスの問題が発生する可能性があるためです。 言い換えると、手動で関数を埋め込むことは、脚を撃つ可能性があります。 ほとんどの場合、コンパイラに関数の埋め込みを委任することをお勧めします。
32ビットおよび64ビット整数
JavaScriptの数値型は
Number
のみであることがよく知られています。
ただし、V8はC ++で実装されているため、JavaScriptの数値の基本的なタイプは選択の問題です。
整数の場合(つまり、小数点なしでJSで数値を指定する場合)、V8はすべての数値が32ビットであると見なします-それらがそうでなくなる限り。 多くの場合、数値は2147483648〜2147483647の範囲にあるため、これは公平な選択のようです。 JS番号(全体)が2147483647を超える場合、JITコンパイラーは、数値の基本型を倍精度型(浮動小数点)に動的に変更する必要があります-これは、潜在的に、他の最適化に特定の影響を与える可能性があります。
このテストでは、3つのシナリオを検討します。
- 32ビットの範囲に収まる数値(合計が小さい)でのみ機能する関数。
- 32ビットの数値と、それらを表すために倍精度データ型(小から大)を必要とする数値の組み合わせで機能する関数。
- 倍精度数(すべて大きい)でのみ動作する関数。
→ GitHubでコードをテストする
![](https://habrastorage.org/getpro/habr/post_images/a64/787/126/a6478712617090d51b812b4d0acc0458.png)
この図から、ノード6(V8 5.1)、ノード8(V8 5.8)、またはノードの将来のバージョンについても、上記の観察結果は有効であると言えます。 つまり、2147483647より大きい整数を使用した計算は、関数が最大値の半分または3分の2の領域にある速度で実行されるという事実につながります。 したがって、デジタルIDが長い場合は、文字列に入れてください。
さらに、ノード6(V8 5.1)およびノード8.1および8.2(V8 5.8)では、ノード8.3+(V8 5.9+)よりも32ビットの範囲内の数値での操作がはるかに高速に実行されることが非常に顕著です。 ) ただし、Node 8.3+(V8 5.9+)の倍精度数の操作は高速です。 これはおそらく、32ビット数の処理速度が遅いためであり、テストコードで使用される関数または
for
ループの呼び出し速度には適用されません。
Jakob Kummerov 、 Yang Guo 、およびV8チームは、このテストの結果をより正確に、より正確にするのに役立ちました。 これについて彼らに感謝しています。
オブジェクトプロパティの列挙
オブジェクトのすべてのプロパティの値を取得し、それらのアクションを実行することは一般的なタスクです。 それを解決する方法はたくさんあります。 V8とNodeの調査済みのバージョンの中で、どのメソッドが最速かを調べます。
V8のすべてのテスト済みバージョンが受けた4つのテストは次のとおりです。
-
hasOwnProperty
を使用してfor-in
ループを使用し、プロパティがオブジェクト(for-in)のプロパティであるかどうかを判断します。
-
Object.keys
を使用し、Array
オブジェクトのreduce
メソッドを使用してキーを列挙します。 プロパティ値へのアクセスは、reduce
渡された反復関数内で実行されます(Object.keysの機能)。
-
Object.keys
を使用し、Array
オブジェクトのreduce
メソッドを使用してキーを列挙します。 プロパティ値へのアクセスは、reduce
渡された矢印イテレータ関数内で実行されます(Object.keysは矢印で機能します)。
-
for
ループでObject.keys
から返された配列を繰り返し処理します。 オブジェクトのプロパティ値へのアクセスは、同じサイクルで実行されます(forループ付きのObject.keys)。
さらに、V8バージョン5.8、5.9、6.0、および6.1の3つの追加テストを実施しました。
-
Object.values
を使用し、Array
オブジェクトのreduce
メソッドを使用してオブジェクトのプロパティ値をObject.values
します(Object.values機能)。
-
Object.values
を使用し、Array
オブジェクトのreduce
メソッドを使用して値を列挙し、reduce
メソッドに渡される反復子関数は矢印関数でした(Object.valuesは矢印で機能します)。
-
for
ループでObject.values
から返された配列を反復処理します(for
ループ付きのObject.values)。
このバージョンはEcmaScript 2017
Object.values
組み込みメソッドをサポートしていないため、V8 5.1(ノード6)ではこれらのテストを実行しませんでした。
→ GitHubでコードをテストする
![](https://habrastorage.org/getpro/habr/post_images/21a/4b9/e92/21a4b9e92ab05e547e050acedf68d5d6.png)
Node 6(V8 5.1)およびNode 8.0-8.2(V8 5.8)では、
for-in
ループを使用することが、間違いなくオブジェクトのキーを反復処理し、そのプロパティ値にアクセスする最速の方法です。 40 , 5 , ,
Object.keys
, 8 .
V8 6.0 (Node 8.3)
for-in
- , . , .
V8 6.1 ( , Node), ,
Object.keys
, ,
for-in
, , ,
for-in
V8 5.1 5.8 (Node 6, Node 8.0-8.2).
, TurboFan — , . , , .
Object.values
,
Object.keys
. , , . , , .
, ,
for-in
- , . , .
JS — , , .
:
- (literal).
- EcmaScript 2015 (class).
- - (constructor).
→ GitHub
![](https://habrastorage.org/getpro/habr/post_images/a2d/be6/7d7/a2dbe67d78552f5accdd36ce2e4a0074.png)
Node 6 (V8 5.1) .
Node 8.0-8.2 (V8 5.8), EcmaScript 2015, , -. , , Node.
V8 5.9 .
, V8 6.0 (, Node 8.3 8.4) 6.1 ( V8 Node), . 500 ! .
![](https://habrastorage.org/getpro/habr/post_images/000/21f/f6c/00021ff6cf5b26eb78adc525a9d2980e.gif)
, . , , . , , , ( ).
, , TurboFan . .
(, ), , . . , . , , , , - . , , , . .
:
- , , (polymorphic with literal).
- , , (polymorphic with constructor).
- (monomorphic string).
- , (monomorphic obj literal).
- , (monomorphic obj with constructor).
→ GitHub
![](https://habrastorage.org/getpro/habr/post_images/3f4/f1e/79e/3f4f1e79e6366de5a24076ac58cf2eb0.png)
, V8.
V8 6.1 ( , Node) , . , , node-v8, « » V8, V8 6.1.
, , , , , . , , , , API .
, V8 , , ,
d8
. , Node. , , Node ( , Node V8). . , .
debugger
, ,
debugger
.
-. .
:
- ,
debugger
(with debugger). - ,
debugger
(without debugger).
→ GitHub
![](https://habrastorage.org/getpro/habr/post_images/174/193/ba5/174193ba594fb14476bb16333749d4de.png)
. V8
debugger
.
, without debugger V8.
:
, , V8 . Node.js, , Pino .
, 10 ( — ) Node.js 6.11 (Crankshaft).
![](https://habrastorage.org/getpro/habr/post_images/8ff/d9a/c17/8ffd9ac1711f6fba74e03faa9db8193f.png)
— , V8 6.1 (TurboFan).
![](https://habrastorage.org/getpro/habr/post_images/768/715/f86/768715f8643d3b1fcfb174dd6ecca3d2.png)
, , Winston JIT- TurboFan. , , , , . Crankshaft TurboFan, , Crankshaft, TurboFan . Winston, , , , Crankshaft, TurboFan. , Pino Crankshaft. .
まとめ
, , V8 5.1, 5.8 5.9, TurboFan V8 6.0 6.1. , , , , , .
TurboFan (V8 6.0 ). TurboFan , , , « V8» . (Chrome) (Node) . , , , . , . , TurboFan (, Winston Pino).
- JavaScript, , , , - , - . JS-, , V8, .
親愛なる読者! JavaScript ?